Here's my own recursive descent parser (based on the not-quite-correct quiz tests): #!/usr/bin/env ruby -wKU require "strscan" # http://json.org/ class JSONParser AST = Struct.new(:value) def parse(input) @input = StringScanner.new(input.strip) parse_value.value end private def parse_value parse_object or parse_array or parse_string or parse_number or parse_keyword or error("Illegal JSON value") end def parse_object if @input.scan(/\{\s*/) object = Hash.new while key = parse_string @input.scan(/\s*:\s*/) or error("Expecting object separator") object[key.value] = parse_value.value @input.scan(/\s*,\s*/) or break end @input.scan(/\s*\}\s*/) or error("Unclosed object") AST.new(object) else false end end def parse_array if @input.scan(/\[\s*/) array = Array.new while contents = parse_value rescue nil array << contents.value @input.scan(/\s*,\s*/) or break end @input.scan(/\s*\]\s*/) or error("Unclosed array") AST.new(array) else false end end def parse_string if @input.scan(/"/) string = String.new while contents = parse_string_content || parse_string_escape string << contents.value end @input.scan(/"\s*/) or error("Unclosed string") AST.new(string) else false end end def parse_string_content @input.scan(/[^\\"]+/) and AST.new(@input.matched) end def parse_string_escape if @input.scan(%r{\\["\\/]}) AST.new(@input.matched[-1]) elsif @input.scan(/\\[bfnrt]/) AST.new(eval(%Q{"#{@input.matched}"})) elsif @input.scan(/\\u[0-9a-fA-F]{4}/) AST.new([Integer("0x#{@input.matched[2..-1]}")].pack("U")) else false end end def parse_number @input.scan(/-?(?:0|[1-9]\d*)(?:\.\d+(?:[eE][+-]?\d+)?)?\b/) and AST.new(eval(@input.matched)) end def parse_keyword @input.scan(/\b(?:true|false|null)\b/) and AST.new(eval(@input.matched.sub("null", "nil"))) end def error(message) if @input.eos? raise "Unexpected end of input." else raise "#{message}: #{@input.peek(@input.string.length)}" end end end __END__ James Edward Gray II