# \[QUIZ\] Parsing JSON (#155)

**URL:** <https://rubytalk.org/t/quiz-parsing-json-155/44332>\
**Category:** ruby-talk\
**Created:** [7 February 2008 04:06 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44332 "2008-02-07T04:06:39Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![James\_Koppel1](https://avatars.discourse-cdn.com/v4/letter/j/e274bd/32.png) [@James\_Koppel1](https://rubytalk.org/u/James_Koppel1)\
**Post date:** [7 February 2008 04:06 UTC](https://rubytalk.org/t/quiz-parsing-json-155/44332/1 "2008-02-07T04:06:39Z")

</div>

class StringIO  
&nbsp;&nbsp;def strip!  
&nbsp;&nbsp;&nbsp;&nbsp;ch = read(1)  
&nbsp;&nbsp;&nbsp;&nbsp;ch = read(1) while /\s/ =~ ch  
&nbsp;&nbsp;&nbsp;&nbsp;ungetc ch[0]  
&nbsp;&nbsp;&nbsp;&nbsp;self  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;def sees?(str1)  
&nbsp;&nbsp;&nbsp;&nbsp;str2 = read(str1.length)  
&nbsp;&nbsp;&nbsp;&nbsp;str2.reverse.scan(/./) {|ch| ungetc ch[0]}  
&nbsp;&nbsp;&nbsp;&nbsp;str2 == str1  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;alias oldread read  
&nbsp;&nbsp;def read(expr,\*buffer)  
&nbsp;&nbsp;&nbsp;&nbsp;return oldread(expr,\*buffer) unless expr.is\_a? Regexp  
&nbsp;&nbsp;&nbsp;&nbsp;str = ""  
&nbsp;&nbsp;&nbsp;&nbsp;str \<\< getc until eof? or (expr =~ str) == 0  
&nbsp;&nbsp;&nbsp;&nbsp;if eof? and (expr =~ str) != 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str.reverse.scan(/./){|ch| ungetc ch[0]}  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return nil  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;maxmtch = str  
&nbsp;&nbsp;&nbsp;&nbsp;until eof?  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str \<\< getc until eof? or expr.match(str)[0] != str  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str \<\< getc until eof? or expr.match(str)[0] == str  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxmtch = expr.match(str)[0]  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;str[maxmtch.length..-1].reverse.scan(/./){|ch|ungetc ch[0]}  
&nbsp;&nbsp;&nbsp;&nbsp;str = maxmtch  
&nbsp;&nbsp;&nbsp;&nbsp;if expr.match(str)[0]==str  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ungetc str[-1]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str[0...-1]  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
end

class JSONParser  
&nbsp;&nbsp;  
&nbsp;&nbsp;def parse(str)  
&nbsp;&nbsp;&nbsp;&nbsp;parse\_next(StringIO.new(str))  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;private  
&nbsp;&nbsp;  
&nbsp;&nbsp;def parse\_next(strio)  
&nbsp;&nbsp;&nbsp;&nbsp;strio.strip!  
&nbsp;&nbsp;&nbsp;&nbsp;if el=number(strio)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return el  
&nbsp;&nbsp;&nbsp;&nbsp;elsif el=string(strio)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return el  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;{"true"=\>true,"false"=\>false,"null"=\>nil}.each\_pair do |k,v|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if strio.sees? k  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strio.read k.length  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return v  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;if strio.sees?("{")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj = Hash.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strio.getc  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;until strio.strip!.sees?("}")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;key = string(strio) or raise  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strio.strip!  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise unless strio.read(1) == ':'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;val = parse\_next(strio)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj[key] = val  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strio.strip!  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise unless strio.sees?('}') or strio.read(1) == ','  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strio.getc  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj  
&nbsp;&nbsp;&nbsp;&nbsp;elsif strio.read(1) == '['  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arr = Array.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;until strio.strip!.sees?("]")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arr \<\< parse\_next(strio)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise unless strio.sees?("]") or strio.read(1) == ','  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strio.getc  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arr  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;def string(strio)  
&nbsp;&nbsp;&nbsp;&nbsp;str=strio.read(/\"([^\"\\]|\\\"|\\\\|\\\/|\\b|\\f|\\n|\\r|\\t|\\u[a-fA-F0-9]{4})\*\"/).  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to\_s.gsub(/\\u([a-fA-F0-9]{4})/){$1.to\_i(16).chr}[1...-1] \  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or return nil  
&nbsp;&nbsp;&nbsp;&nbsp;str.gsub!(/\\[\"\\\/bfnrt]/){|s|eval("\"#{s}\"")}  
&nbsp;&nbsp;&nbsp;&nbsp;str  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;def number(strio)  
&nbsp;&nbsp;&nbsp;&nbsp;eval (strio.read(/-?(0|[1-9]\d\*)(\.\d\*)?([eE][+-]?\d+)?/).to\_s)  
&nbsp;&nbsp;end  
end

> **···**
>
> \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> Be a better friend, newshound, and  
> know-it-all with Yahoo! Mobile. Try it now. [http://mobile.yahoo.com/;\_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ](http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ)
