Skip to content

Instantly share code, notes, and snippets.

@cb341
Created March 14, 2026 16:50
Show Gist options
  • Select an option

  • Save cb341/9ab4e27c8108c1496f909e31c85ef73d to your computer and use it in GitHub Desktop.

Select an option

Save cb341/9ab4e27c8108c1496f909e31c85ef73d to your computer and use it in GitHub Desktop.
A simple script to analyse JSON schema
#!/usr/bin/env ruby
# describe_json - Parses JSON files and displays a detailed, human-readable
# breakdown of the schema including data types, field names, array sizes,
# and sample values. Supports file arguments or stdin input.
# author: claude-haiku
require 'json'
def show_help
puts <<~HELP
describe_json - Analyze and describe JSON file structure
Usage:
ruby describe_json.rb [FILE]
cat FILE | ruby describe_json.rb
ruby describe_json.rb < FILE
Options:
--help Show this help message
Examples:
ruby describe_json.rb data.json
cat config.json | ruby describe_json.rb
HELP
exit 0
end
def format_type(value)
case value
when String
truncated = value.length > 40 ? "#{value[0..40]}..." : value
"string[#{value.length}] = #{truncated.inspect}"
when Integer
"int = #{value}"
when Float
"float = #{value}"
when TrueClass, FalseClass
"bool = #{value}"
when NilClass
"null"
else
value.class.to_s.downcase
end
end
def describe_value(value, indent = 0, add_comma = false)
prefix = " " * indent
inner = " " * (indent + 1)
case value
when Hash
puts "{"
keys = value.keys
keys.each_with_index do |key, idx|
is_last = idx == keys.length - 1
val = value[key]
comma = is_last ? "" : ","
case val
when Hash, Array
print "#{inner}#{key.inspect}: "
describe_value(val, indent + 1, !is_last)
else
puts "#{inner}#{key.inspect}: #{format_type(val)}#{comma}"
end
end
puts "#{prefix}}#{add_comma ? ',' : ''}"
when Array
if value.empty?
puts "[]#{add_comma ? ',' : ''}"
else
first = value.first
puts "["
case first
when Hash
puts "#{inner}{"
first.each_with_index do |(k, v), idx|
is_last = idx == first.keys.length - 1
comma = is_last ? "" : ","
case v
when Hash, Array
print "#{inner} #{k.inspect}: "
describe_value(v, indent + 2, !is_last)
else
puts "#{inner} #{k.inspect}: #{format_type(v)}#{comma}"
end
end
puts "#{inner}}"
else
puts "#{inner}#{format_type(first)}"
end
if value.length > 1
all_same = value.all? { |item| item.class == first.class }
if all_same && first.is_a?(Hash)
same_keys = value.all? { |item| item.keys.sort == first.keys.sort }
puts "#{inner}... #{value.length} items total" if same_keys
end
end
puts "#{prefix}]#{add_comma ? ',' : ''}"
end
else
puts format_type(value) + (add_comma ? ',' : '')
end
end
def summarize_structure(value)
case value
when Hash
keys = value.keys
"{ #{keys.map { |k| "#{k}: #{summarize_structure(value[k])}" }.join(', ')} }"
when Array
if value.empty?
"[]"
else
first_summary = summarize_structure(value.first)
value.length == 1 ? "[#{first_summary}]" : "[#{first_summary} (#{value.length} items)]"
end
when String
"string(#{value.length})"
when Integer
"int"
when Float
"float"
when TrueClass, FalseClass
"bool"
when NilClass
"null"
else
value.class.to_s.downcase
end
end
show_help if ARGV.include?('--help')
input = if ARGV.length > 0
filename = ARGV[0]
unless File.exist?(filename)
puts "Error: File not found - #{filename}"
exit 1
end
File.read(filename)
else
STDIN.read
end
begin
data = JSON.parse(input)
puts "=== JSON Structure Description ==="
puts
puts "Root Type: #{data.class}"
puts
case data
when Array
puts "Array with #{data.length} items"
puts "\nFirst Item Structure:"
describe_value(data.first, 0)
if data.length > 1
puts
all_same = data.all? { |item| item.class == data.first.class }
if all_same && data.first.is_a?(Hash)
same_keys = data.all? { |item| item.keys.sort == data.first.keys.sort }
puts "✓ All #{data.length} items have consistent structure" if same_keys
puts "⚠ Items have varying structures" unless same_keys
end
end
when Hash
puts "Structure:"
describe_value(data, 0)
else
puts "Value: #{data.inspect}"
end
puts "\n=== Compact Schema ==="
puts summarize_structure(data)
rescue JSON::ParserError => e
puts "Error: Invalid JSON - #{e.message}"
exit 1
rescue StandardError => e
puts "Error: #{e.message}"
exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment