Code archives/File Utilities/tiny JSON reader/writer

This code has been declared by its author to be Public Domain code.

Download source code

tiny JSON reader/writer by Warpy2008
from http://www.json.org :
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.


This is a tiny JSON library I wrote in about an hour and a half. Amazingly, it worked first time!
Type jsondecoder
	Field txt$
	Field i
	Field curchr$
	Field things:TList
	
	Method New()
		things=New TList
	End Method

	Method getnext(tokens$[],onlywhitespace=1)
		oldi=i
		While i<Len(txt)
			c$=Chr(txt[i])
			i:+1
			For token$=EachIn tokens
				If c=token 
					curchr=c
					Return 1
				EndIf
			Next
			If onlywhitespace And (Not (c=" " Or c="~t" Or c="~n" Or c="~r"))
				i:-1
				Return 0
			EndIf
		Wend
		i=oldi
		Return 0
	End Method
	
	Function Create:jsondecoder(txt$)
		j:jsondecoder=New jsondecoder
		j.txt=txt
		j.i=i
		Return j
	End Function
	
	Method parse()
		While getnext(["{","["])
			Select curchr
			Case "{" 'new object
				o:jsonobject=parseobject()
				If Not o
					Print "error - couldn't parse object"
				EndIf
				things.addlast o
			Case "[" 'new array
				a:jsonarray=parsearray()
				If Not a
					Print "error - couldn't parse array"
				EndIf
				things.addlast a
			End Select
		Wend
	End Method	
	
	Method parseobject:jsonobject()
		o:jsonobject=New jsonobject
		While getnext(["~q","}"])
			Select curchr
			Case "~q"
				p:jsonpair=parsepair()
				If Not p
					Print "error reading pair"
				EndIf
				o.pairs.addlast p
				If Not getnext([",","}"])
					Print "error after reading pair - expected either , or }"
				EndIf
				If curchr="}"
					Return o
				EndIf
			Case "}"
				Return o
			End Select
		Wend
		
		Print "error reading Object - expected a } at least!"
	End Method
	
	Method parsepair:jsonpair()
		p:jsonpair=New jsonpair
		p.name=parsestring()
		If Not getnext([":"])
			Print "error reading pair - expected a :"
		EndIf
		v:jsonvalue=parsevalue()
		If Not v
			Print "error reading pair - couldn't read a value"
		EndIf
		p.value=v
		Return p
	End Method
	
	Method parsearray:jsonarray()
		a:jsonarray=New jsonarray
		While getnext(["~q","-","0","1","2","3","4","5","6","7","8","9","{","[","t","f","n","]"])
			Select curchr
			Case "~q","-","0","1","2","3","4","5","6","7","8","9","{","[","t","f","n"
				i:-1
				v:jsonvalue=parsevalue()
				a.values.addlast v
				If Not getnext([",","]"])
					Print "error - expecting , or ]"
				EndIf
				If curchr="]"
					Return a
				EndIf
				
			Case "]"
				Return a
			End Select
		Wend
		Print "error - expecting a value or ]"
	End Method
	
	Method parsestring$()
		oldi=i
		s$=""
		
		While getnext(["~q","\"],0)
			s:+txt[oldi..i-1]
			Select curchr
			Case "~q"
				Return s
			Case "\"
				Select Chr(txt[i])
				Case "~q"
					s:+"~q"
				Case "\"
					s:+"\"
				Case "/"
					s:+"/"
				Case "b"
					s:+Chr(8)
				Case "f"
					s:+Chr(12)
				Case "n"
					s:+"~n"
				Case "r"
					s:+"~r"
				Case "t"
					s:+"~t"
				Case "u"
					s:+parseunicode()
				End Select
				i:+1
			End Select
			oldi=i
		Wend
	End Method
	
	Method parseunicode$()
		n:Short=0
		For t=1 To 4
			n:*16
			c=txt[i+t]
			If c>48 And c<57
				n:+c-48
			ElseIf c>=65 And c<=70
				n:+c-55
			ElseIf c>=97 And c<=102
				n:+c-87
			EndIf
		Next
		i:+4
		Return Chr(n)
	End Method
	
	Method parsevalue:jsonvalue()
		If Not getnext(["~q","-","0","1","2","3","4","5","6","7","8","9","{","[","t","f","n"])
			Print "error - expecting the beginning of a value"
		EndIf
		Select curchr
		Case "~q"
			s$=parsestring()
			Return jsonstringvalue.Create(s,0)
		Case "-","0","1","2","3","4","5","6","7","8","9"
			n:Double=parsenumber()
			Return jsonnumbervalue.Create(n)
		Case "{"
			o:jsonobject=parseobject()
			Return o
		Case "["
			a:jsonarray=parsearray()
			Return a
		Case "t"
			i:+3
			Return jsonliteralvalue.Create(1)
		Case "f"
			i:+4
			Return jsonliteralvalue.Create(0)
		Case "n"
			i:+2
			Return jsonliteralvalue.Create(-1)
		End Select
	End Method
	
	Method parsenumber:Double()
		i:-1
		sign=1
		n:Double=0
		Select Chr(txt[i])
		Case "-"
			i:+2
			Return parsenumber()*(-1)
		Case "0"
			i:+1
			If getnext(["."])
				n=parsefraction()
			EndIf
		Case "1","2","3","4","5","6","7","8","9"
			n=parseinteger()
			If getnext(["."])
				n:+parsefraction()
			EndIf
		End Select
		
		If Chr(txt[i])="e" Or Chr(txt[i])="E"
			i:+1
			Select Chr(txt[i])
			Case "+"
				sign=1
			Case "-"
				sign=-1
			Default
				Print "error - not a + or - when reading exponent in number"
			End Select
			e=parseinteger()
			n:*10^(sign*e)
		EndIf
		Print "parsed number "+String(n)
		Return n
	End Method
			
	Method parsefraction:Double()
		digits=0
		n:Double=0
		While txt[i]>=48 And txt[i]<=57 And i<Len(txt)
			n:*10
			n:+txt[i]-48
			i:+1
			digits:+1
		Wend
		n:/(10^digits)
		If i=Len(txt)
			Print "error - reached EOF while reading number"
		EndIf
		Print "parsed fraction "+String(n)
		Return n
	End Method
	
	Method parseinteger:Double()
		n:Double=0
		While txt[i]>=48 And txt[i]<=57 And i<Len(txt)
			n:*10
			n:+txt[i]-48
			i:+1
		Wend
		If i=Len(txt)
			Print "error - reached EOF while reading number"
		EndIf
		Print "parsed integer "+String(n)
		Return n
	End Method
			
End Type

Type jsonvalue

	Method repr$(tabs$="")
		Return tabs
	End Method
End Type

Type jsonobject Extends jsonvalue
	Field pairs:TList

	Method New()
		pairs=New TList
	End Method
	
	Method addnewpair(txt$,value:jsonvalue)
		pairs.addlast jsonpair.Create(txt,value)
	End Method
	
	Method repr$(tabs$="")
		t$="{"
		ntabs$=tabs+"~t"
		op:jsonpair=Null
		For p:jsonpair=EachIn pairs
			If op Then t:+","
			t:+"~n"+ntabs+p.repr(ntabs)
			op=p
		Next
		t:+"~n"+tabs+"}"
		Return t
	End Method
	
	Method getvalue:jsonvalue(name$)
		For p:jsonpair=EachIn pairs
			If p.name=name
				Return p.value
			EndIf
		Next
	End Method
	
	Method getstringvalue$(name$)
		v:jsonstringvalue=jsonstringvalue(getvalue(name))
		If v
			Return v.txt
		EndIf
	End Method
	
	Method getnumbervalue:Double(name$)
		v:jsonnumbervalue=jsonnumbervalue(getvalue(name))
		If v
			Return v.number
		EndIf
	End Method
	
	Method getliteralvalue(name$)
		v:jsonliteralvalue=jsonliteralvalue(getvalue(name))
		If v
			Return v.value
		EndIf
	End Method
	
	Method getarrayvalue:jsonarray(name$)
		v:jsonarray=jsonarray(getvalue(name))
		Return v
	End Method
	
	Method getobjectvalue:jsonobject(name$)
		v:jsonobject=jsonobject(getvalue(name))
		Return v
	End Method
				
	
End Type

Type jsonpair
	Field name$,value:jsonvalue

	Function Create:jsonpair(name$,value:jsonvalue)
		p:jsonpair=New jsonpair
		p.name=name
		p.value=value
		Return p
	End Function

	Method repr$(tabs$="")
		t$="~q"+name+"~q : "
		For i=1 To (Len(t)+7)/8
			tabs:+"~t"
		Next
		middo$=""
		For i=1 To (8-(Len(t) Mod 8))
			middo:+" "
		Next
		Return t+middo+value.repr(tabs)
	End Method
End Type

Type jsonarray Extends jsonvalue
	Field values:TList
	
	Method New()
		values=New TList
	End Method
	
	Method repr$(tabs$="")
		t$="["
		ntabs$=tabs+"~t"
		ov:jsonvalue=Null
		For v:jsonvalue=EachIn values
			If ov Then t:+","
			t:+"~n"+ntabs+v.repr(ntabs)
			ov=v
		Next
		t:+"~n"+tabs+"]"
		Return t
	End Method
End Type


Type jsonstringvalue Extends jsonvalue
	Field txt$
	
	Function Create:jsonstringvalue(txt$,pretty=1)
		jsv:jsonstringvalue=New jsonstringvalue
		
		If pretty
			otxt$=""
			i=0
			For i=0 To Len(txt)-1
				Select Chr(txt[i])
				Case "~q"
					otxt:+"\~q"
				Case "\"
					otxt:+"\\"
				Case "/"
					otxt:+"\/"
				Case Chr(8)
					otxt:+"\b"
				Case Chr(12)
					otxt:+"\f"
				Case "~n"
					otxt:+"\n"
				Case "~r"
					otxt:+"\r"
				Case "~t"
					otxt:+"\t"
				Default
					otxt:+Chr(txt[i])
				End Select
			Next
			jsv.txt=otxt
		Else
			jsv.txt=txt
		EndIf
		Return jsv
	End Function
	
	Method repr$(tabs$="")
		Return "~q"+txt+"~q"
	End Method
End Type

Type jsonnumbervalue Extends jsonvalue
	Field number:Double
	
	Function Create:jsonnumbervalue(n:Double)
		jnv:jsonnumbervalue=New jsonnumbervalue
		jnv.number=n
		Return jnv
	End Function
	
	Method repr$(tabs$="")
		Return String(number)
	End Method
End Type

Type jsonliteralvalue Extends jsonvalue
	Field value
	'1 - true
	'0 - false
	'-1 - nil
	
	Function Create:jsonliteralvalue(value)
		jlv:jsonliteralvalue=New jsonliteralvalue
		jlv.value=value
		Return jlv
	End Function
	
	Method repr$(tabs$="")
		Select value
		Case 1
			Return "true"
		Case 0
			Return "false"
		Case -1
			Return "nil"
		End Select
	End Method
End Type



'EXAMPLE
f:TStream=ReadFile("json.txt")
txt$=""
While Not Eof(f)
	txt:+f.ReadLine()
Wend

j:jsondecoder=jsondecoder.Create(txt)
j.parse()


For v:jsonvalue=EachIn j.things
	Print v.repr()
Next

Comments

plash2008
Woah.. now we have three!


Warpy2008
Yep!


Bobysait2010
It's a bmx file, not bb.
Whatever, nice job


TomToadApril
Thanks for the code, almost exactly what I needed. Not SuperStrict compatible, so here is your code modified to be SuperStrict.



Code Archives Forum