Code archives/File Utilities/JSON Reader/Writer
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
** UPDATED: 2 May 2017 fixed: off-by-one error with arrays. Element counter incorrectly used "," as a counter. JSON is short for JavaScript Object Notation, and is very nice for config files and the like (or a more readable alternative to XML files) It looks something like this: { string: 'abc', number: 1.0, boolean: true, object: { field: null, morestuff: "blablabla" }, array: [1,2,3] } Go here for more info on JSON http://json.org More info on usage, see the top of the source. Please let me know if there is anything missing or incorrect =) | |||||
Rem ********************************************************************************************************************************** * JSON Reader/Writer and generic handling * ********************************************************************************************************************************** * TJSONValue is the generic container for all JSON data types * Type TJSONValue ' determines the type of value Field Class:Int ' returns value by index (only valid for arrays) Method GetByIndex:TJSONValue( index:Int) ' returns value by name (only valid for objects) Method GetByName:TJSONValue( name:Object) Method SetByIndex( index:Int, value:TJSONValue) Method SetByName( name:Object, value:TJSONValue) ' lookup value by string (either a number or a name, valid for arrays & objects) Method LookupValue:TJSONValue( value:Object) ' returns properly indented source representation of JSON data Method ToSource:String( level:Int = 0) ' returns JSON data as string Method ToString:String() EndType Type TJSONNumber Extends TJSONValue Field Value:Double EndType Type TJSONString Extends TJSONValue Field Value:String EndType Type TJSONBoolean Extends TJSONValue Field Value:Int EndType Type TJSONObject Extends TJSONValue Field Items:TMap ' holds actual fields Field List:TList ' holds field order EndType Type TJSONArray Extends TJSONValue Field Items:TJSONValue[] EndType The methods are implemented in the various subclasses. ********************************************************************************************************************************** * TJSON handles all reading/writing of json data, and allows for easy acces to elements via "paths" * Type TJSON ' the root JSON value Field Root:TJSONValue ' create a new JSON from any source Function Create:TJSON( source:Object) ' read JSON data from a TJSONValue, TStream or String Method Read:TJSONValue( source:Object) ' write JSON data to a TStream or a file (as String) Method Write( dest:Object) ' parses a string into its JSON data representation Method ParseString:TJSONValue( s:Object) ' lookup a JSON value at at specified path, returns NULL on failure Method Lookup:TJSONValue( path:String) ' sets a JSON value at path to value, value can be a TJSONValue or JSON data as a string Method SetValue( path:String, value:Object) ' returns the json value at specified Method GetValue:TJSONValue( path:String) ' returns blitz specific types from specified paths Method GetNumber:Double( path:String) Method GetString:String( path:String) Method GetBoolean:Int( path:String) ' returns only these specific objects Method GetObject:TJSONObject( path:String) Method GetArray:TJSONArray( path:String) ' get a blitz array from a JSON array or NULL on failure Method GetArrayInt:Int[]( path:String) Method GetArrayDouble:Double[]( path:String) Method GetArrayString:String[]( path:String) EndType ********************************************************************************************************************************** * PATHS * identifiers are seperated with ".", and has special syntax for array indices example: "users.joe.age" ' direct access "users.joe.medals.0" ' array index, arrays are 0 based ********************************************************************************************************************************** * usage example: * Local json:TJSON = TJSON.Create( "{ string: 'abc', number: 1.0, boolean: true, object: { field: null }, array: [1,2,3] }") Print json.GetValue("string").ToString() ' prints "abc" Print json.GetValue("object.field").ToString() ' prints "null" Print json.GetValue("array").ToString() ' prints "[1,2,3]" Print json.GetValue("array.1").ToString() ' prints "2" ********************************************************************************************************************************** * NOTES * ** most of the TJSON.GetXXX methods returns the JSON NULL type on failure if not specified ** all identifiers are CASE SENSITIVE ** the parser is as close as i could get it with my current understanding of JSON, please let me know if i missed something ** see the bottom of this source file for more examples, or check out http://json.org/ for more info on JSON *********************************************************** * INFO * author: grable email : grable0@gmail.com EndRem SuperStrict Import BRL.LinkedList Import BRL.Map ' ' JSON value classes ' Const JSON_NULL:Int = 1 Const JSON_OBJECT:Int = 2 Const JSON_ARRAY:Int = 3 Const JSON_STRING:Int = 4 Const JSON_NUMBER:Int = 5 Const JSON_BOOLEAN:Int = 6 ' ' used by TJSON / TJSONObject for identifier lookup ' Type TJSONKey Field Value:String Method ToString:String() Return "~q" + Value + "~q" EndMethod Method Compare:Int( o:Object) Local key:TJSONKey = TJSONKey(o) If key Then Return Value.Compare( key.Value) If String(o) Then Return Value.Compare( o) Return 1 EndMethod EndType ' ' JSON Value objects ' Type TJSONValue Abstract Field Class:Int Method GetByIndex:TJSONValue( index:Int) Return Null EndMethod Method GetByName:TJSONValue( name:Object) Return Null EndMethod Method SetByIndex( index:Int, value:TJSONValue) EndMethod Method SetByName( name:Object, value:TJSONValue) EndMethod Method LookupValue:TJSONValue( value:Object) Return Self EndMethod Method ToSource:String( level:Int = 0) Abstract EndType Type TJSONObject Extends TJSONValue Field Items:TMap = New TMap Field List:TList = New TList ' for keeping the order of fields Method New() Class = JSON_OBJECT EndMethod Method ToString:String() Local s:String, lines:Int = 0 If List.Count() <= 0 Then Return "{}" For Local o:TNode = EachIn List If lines > 0 Then s :+ ", " s :+ o._key.ToString() +": " Local jsv:TJSONValue = TJSONValue(o._value) If jsv.Class = JSON_STRING Then s :+ jsv.ToSource() Else s :+ jsv.ToString() EndIf lines :+ 1 Next Return "{ "+ s +" }" EndMethod Method ToSource:String( level:Int = 0) Local s:String, lines:Int = 0 If List.Count() <= 0 Then Return "{}" For Local o:TNode = EachIn List If lines > 0 Then s :+ ",~n" + RepeatString( "~t", level + 1) s :+ o._key.ToString() +": "+ TJSONValue(o._value).ToSource( level + 1) lines :+ 1 Next If lines > 1 Then Return "{~n"+ RepeatString( "~t", level + 1) + s + "~n" + RepeatString( "~t", level) + "}" Return "{ "+ s +" }" EndMethod Method GetByName:TJSONValue( name:Object) Return TJSONValue( Items.ValueForKey( name)) EndMethod Method SetByName( name:Object, value:TJSONValue) Local node:TNode If TJSONKey(name) Then Items.Insert( name, value) node = Items._FindNode( name) If Not List.Contains( node) Then List.AddLast( node) ElseIf String(name) Then Local s:String = String(name) If s.Length > 0 Then Items.Insert( s, value) node = Items._FindNode( s) If Not List.Contains( node) Then List.AddLast( node) EndIf EndIf EndMethod Method LookupValue:TJSONValue( value:Object) If TJSONKey(value) Then Return GetByName( value) ElseIf String(value) Then If Not IsNumber( String(value)) Then Return GetByName( value) EndIf EndMethod EndType Type TJSONArray Extends TJSONValue Field Items:TJSONValue[] Field AutoGrow:Int = True Function Create:TJSONArray( size:Int) Local jso:TJSONArray = New TJSONArray jso.Items = New TJSONValue[ size] Return jso EndFunction Method New() Class = JSON_ARRAY EndMethod Method ToString:String() Local s:String, lines:Int = 0 If Items.Length <= 0 Then Return "[]" For Local o:TJSONValue = EachIn Items If lines > 0 Then s :+ ", " If o.Class = JSON_STRING Then s :+ o.ToSource() Else s :+ o.ToString() EndIf lines :+ 1 Next Return "[ "+ s +" ]" EndMethod Method ToSource:String( level:Int = 0) If Items.Length <= 0 Then Return "[]" Local s:String, lines:Int = 0 For Local o:TJSONValue = EachIn Items If lines > 0 Then s :+ ",~n" + RepeatString( "~t", level + 1) s :+ o.ToSource( level + 1) lines :+ 1 Next If lines > 1 Then Return "[~n" + RepeatString( "~t", level + 1) + s + "~n" + RepeatString( "~t", level) + "]" Return "[ "+ s +" ]" EndMethod Method GetByIndex:TJSONValue( index:Int) If (index >= 0) And (index < Items.Length) Then Return TJSONValue( Items[ index]) EndIf EndMethod Method SetByIndex( index:Int, value:TJSONValue) If (index >= 0) And (index < Items.Length) Then Items[ index] = value ElseIf AutoGrow And (Index >= Items.Length) Then Local oldlen:Int = Items.Length Items = Items[..index + 1] For Local i:Int = oldlen Until Items.Length Items[i] = TJSON.NIL Next Items[index] = value EndIf EndMethod Method LookupValue:TJSONValue( value:Object) If TJSONKey(value) Then Local s:String = TJSONKey(value).Value If IsNumber( s) Then Return GetByIndex( s.ToInt()) ElseIf String(value) Then If IsNumber( String(value)) Then Return GetByIndex( String(value).ToInt()) EndIf EndMethod EndType Type TJSONString Extends TJSONValue Field Value:String Method New() Class = JSON_STRING EndMethod Function Create:TJSONString( value:String) Local jso:TJSONString = New TJSONString jso.Value = value Return jso EndFunction Method ToString:String() Return Value EndMethod Method ToSource:String( level:Int = 0) Return "~q" + Value + "~q" EndMethod EndType Type TJSONNumber Extends TJSONValue Field Value:Double Method New() Class = JSON_NUMBER EndMethod Function Create:TJSONNumber( value:Double) Local jso:TJSONNumber = New TJSONNumber jso.Value = value Return jso EndFunction Method ToString:String() Return DoubleToString( Value) EndMethod Method ToSource:String( level:Int = 0) Return DoubleToString( Value) EndMethod EndType Type TJSONBoolean Extends TJSONValue Field Value:Int Method New() Class = JSON_BOOLEAN EndMethod Function Create:TJSONBoolean( value:Int) Local jso:TJSONBoolean = New TJSONBoolean jso.Value = value Return jso EndFunction Method ToString:String() If Value Then Return "true" Return "false" EndMethod Method ToSource:String( level:Int = 0) If Value Then Return "true" Return "false" EndMethod EndType Type TJSONNull Extends TJSONValue Method New() Class = JSON_NULL EndMethod Method ToString:String() Return "null" EndMethod Method ToSource:String( level:Int = 0) Return "null" EndMethod EndType ' ' Parses any string into its JSONValue representation ' Type TJSONParser Const ARRAY_GROW_SIZE:Int = 32 Field Source:String Field Index:Int Field MakeLowerCase:Int Method Parse:TJSONValue() Const OBJECT_START:Byte = Asc("{") Const OBJECT_STOP:Byte = Asc("}") Const ARRAY_START:Byte = Asc("[") Const ARRAY_STOP:Byte = Asc("]") Const FIELD_SEP:Byte = Asc(":") Const ELEM_SEP:Byte = Asc(",") Const IDENT_START1:Byte = Asc("a") Const IDENT_STOP1:Byte = Asc("z") Const IDENT_START2:Byte = Asc("A") Const IDENT_STOP2:Byte = Asc("Z") Const UNDERSCORE:Byte = Asc("_") Const MINUS:Byte = Asc("-") Const NUMBER_START:Byte = Asc("0") Const NUMBER_STOP:Byte = Asc("9") Const NUMBER_SEP:Byte = Asc(".") Const STRING_START1:Byte = Asc("~q") Const STRING_START2:Byte = Asc("'") Const STRING_ESC:Byte = Asc("\") Const SPACE:Byte = Asc(" ") Const TAB:Byte = Asc("~t") Const CR:Byte = Asc("~r") Const LF:Byte = Asc("~n") Local c:Byte ' skip whitspace & crlf While Index < Source.Length c = Source[Index] If (c = SPACE) Or (c = TAB) Or (c = CR) Or (c = LF) Then Index :+ 1 Continue EndIf Exit Wend ' at end allready ? If (Index >= Source.Length) Or (Source[Index] = 0) Then Return Null c = Source[Index] If c = OBJECT_START Then ' OBJECT Local jso:TJSONObject = New TJSONObject Index :+ 1 While Index < Source.Length ' skip whitespace & crlf While Index < Source.Length c = Source[Index] If (c = SPACE) Or (c = TAB) Or (c = CR) Or (c = LF) Then Index :+ 1 Continue EndIf Exit Wend If c = ELEM_SEP Then Index :+ 1 ElseIf c = OBJECT_STOP Index :+ 1 ' return json object Return jso Else Local start:Int = Index, idinstr:Int = False Local name:String If c = STRING_START1 Or c = STRING_START2 Then ' get name enclosed in string tags Local strchar:Byte = c Index :+ 1 start = Index While (Index < Source.Length) And (Source[Index] <> strchar) If Source[Index] = STRING_ESC Then Index :+ 1 EndIf Index :+ 1 Wend name = Source[start..Index] ' escape string 'name = name.Replace( "\/", "/") ' wtf??? name = name.Replace( "\~q", "~q") name = name.Replace( "\'", "'") name = name.Replace( "\t", "~t") name = name.Replace( "\r", "~r") name = name.Replace( "\n", "~n") name = name.Replace( "\\", "\") Index :+ 1 idinstr = True Else ' get name as an identifier Index :+ 1 While Index < Source.Length c = Source[Index] If ((c >= IDENT_START1) And (c <= IDENT_STOP1)) Or ((c >= IDENT_START2) And (c <= IDENT_STOP2)) Or .. ((c >= NUMBER_START) And (c <= NUMBER_STOP)) Or (c = UNDERSCORE) Or (c = MINUS) Then Index :+ 1 Continue EndIf name = Source[start..Index] Exit Wend EndIf ' skip whitespace & crlf While Index < Source.Length c = Source[Index] If (c = SPACE) Or (c = TAB) Or (c = CR) Or (c = LF) Then Index :+ 1 Continue EndIf Exit Wend ' check for field seperator If c <> FIELD_SEP Then Error( "expected field seperator ~q:~q") Return Null EndIf Index :+ 1 ' parse value Local val:TJSONValue = Parse() If val = Null Then Return Null If idinstr Then Local key:TJSONKey = New TJSONKey key.Value = name jso.SetByName( key, val) Else jso.SetByName( name, val) EndIf EndIf Wend ElseIf c = ARRAY_START Then ' ARRAY Local jso:TJSONArray = TJSONArray.Create( ARRAY_GROW_SIZE) Local count:Int = 0 Index :+ 1 While Index < Source.Length ' skip whitespace & crlf While Index < Source.Length c = Source[Index] If (c = SPACE) Or (c = TAB) Or (c = CR) Or (c = LF) Then Index :+ 1 Continue EndIf Exit Wend ' parse value If c = ELEM_SEP Then Index :+ 1 ElseIf c = ARRAY_STOP Then ' return json array Index :+ 1 If count = 0 Then jso.Items = Null Else jso.Items = jso.Items[..count] EndIf Return jso Else Local val:TJSONValue = Parse() If val = Null Then Return Null ' expand array if needed If count >= jso.Items.Length Then jso.Items = jso.Items[..jso.Items.Length+ARRAY_GROW_SIZE] EndIf jso.SetByIndex( count, val) count :+ 1 EndIf Wend ElseIf c = STRING_START1 Or c = STRING_START2 Then ' STRING Local strchar:Byte = c Index :+ 1 Local start:Int = Index While (Index < Source.Length) And (Source[Index] <> strchar) If Source[Index] = STRING_ESC Then Index :+ 1 EndIf Index :+ 1 Wend Index :+ 1 ' escape string Local s:String = Source[start..Index-1] 's = s.Replace( "\/", "/") ' wtf??? s = s.Replace( "\~q", "~q") s = s.Replace( "\'", "'") s = s.Replace( "\t", "~t") s = s.Replace( "\r", "~r") s = s.Replace( "\n", "~n") s = s.Replace( "\\", "\") ' return json string Return TJSONString.Create( s) ElseIf (c >= NUMBER_START) And (c <= NUMBER_STOP) Then ' NUMBER Local start:Int = Index, gotsep:Int = False ' scan for rest of number Index :+ 1 While Index < Source.Length c = Source[Index] If (c >= NUMBER_START) And (c <= NUMBER_STOP) Then Index :+ 1 Continue ElseIf c = NUMBER_SEP Then If gotsep Then Error( "invalid floating point number") Return Null EndIf gotsep = True Index :+ 1 Continue EndIf Exit Wend ' return json number Return TJSONNumber.Create( Source[start..Index].ToDouble()) ElseIf (c >= IDENT_START1) And (c <= IDENT_STOP1) Then ' TRUE FALSE NULL Local start:Int = Index ' scan for rest of identifier While Index < Source.Length c = source[Index] If (c >= IDENT_START1) And (c <= IDENT_STOP1) Then Index :+ 1 Continue EndIf Exit Wend ' validate identifier Local s:String = Source[start..Index] If s = "false" Then Return TJSONBoolean.Create( False) If s = "true" Then Return TJSONBoolean.Create( True) If s = "null" Then Return TJSON.NIL Error( "expected ~qtrue~q,~qfalse~q Or ~qnull~q") Return Null Else DebugLog "unknown character: " + c + " => " + Chr(c) EndIf EndMethod Method Error( msg:String) DebugLog "JSON-PARSER-ERROR[ index:"+Index+" ]: " + msg EndMethod EndType ' ' Main JSON object, allows access to values via paths and for reading/writing ' Type TJSON Global NIL:TJSONValue = New TJSONNull Field Root:TJSONValue = NIL Field LookupKey:TJSONKey = New TJSONKey Function Create:TJSON( source:Object) Local json:TJSON = New TJSON json.Read( source) Return json EndFunction Method Read:TJSONValue( source:Object) Root = NIL If TJSONValue(source) Then ' set root Root = TJSONValue( source) Return Root ElseIf TStream(source) Then ' read strings from stream Local s:String, stream:TStream = TStream(source) While Not stream.Eof() s :+ stream.ReadLine() + "~n" Wend ' parse string Local parser:TJSONParser = New TJSONParser parser.Source = s Root = parser.Parse() If Root Then Return Root Root = NIL ElseIf String(source) Then ' parse string Local parser:TJSONParser = New TJSONParser parser.Source = String(source) Root = parser.Parse() If Root Then Return Root Root = NIL EndIf Return Null EndMethod Method Write( dest:Object) If TStream(dest) Then TStream(dest).WriteString( Root.ToSource()) ElseIf String(dest) Then Local stream:TStream = WriteFile( String(dest)) If Not stream Then Return stream.WriteString( Root.ToSource()) stream.Close() EndIf EndMethod Method ParseString:TJSONValue( s:Object) If TJSONValue(s) Then Return TJSONValue(s) If Not String(s) Then Return NIL Local parser:TJSONParser = New TJSONParser parser.Source = String(s) Local val:TJSONValue = parser.Parse() If val Then Return val Return NIL EndMethod Method Lookup:TJSONValue( path:String) If (path.Length = 0) Or (path.ToLower() = "root") Then Return Root LookupKey.Value = GetNext( path, ".") Local val:TJSONValue = Root.LookupValue( LookupKey) If val Then Local last:TJSONValue = val While path.Length > 0 last = val LookupKey.Value = GetNext( path, ".") val = last.LookupValue( LookupKey) Wend Return val EndIf EndMethod Method SetValue( path:String, value:Object) LookupKey.Value = GetNext( path, ".") Local val:TJSONValue = Root.LookupValue( LookupKey) If val Then Local last:TJSONValue = Root While (path.Length > 0) And val last = val LookupKey.Value = GetNext( path, ".") val = last.LookupValue( LookupKey) Wend If (last.Class = JSON_ARRAY) And IsNumber( LookupKey.Value) Then last.SetByIndex( LookupKey.Value.ToInt(), ParseString(value)) ElseIf (last.Class = JSON_OBJECT) And (Not IsNumber( LookupKey.Value)) Then last.SetByName( LookupKey.Value, ParseString(value)) EndIf Else If (Root.Class = JSON_ARRAY) And IsNumber( LookupKey.Value) Then Root.SetByIndex( LookupKey.Value.ToInt(), ParseString(value)) ElseIf (Root.Class = JSON_OBJECT) And (Not IsNumber( LookupKey.Value)) Then Root.SetByName( LookupKey.Value, ParseString(value)) EndIf EndIf EndMethod Method GetValue:TJSONValue( path:String) Local val:TJSONValue = Lookup( path) If val Then Return val Return NIL EndMethod Method GetNumber:Double( path:String) Local val:TJSONValue = Lookup( path) If val And val.Class = JSON_NUMBER Then Return TJSONNumber(val).Value Return 0.0 EndMethod Method GetString:String( path:String) Local val:TJSONValue = Lookup( path) If val And val.Class = JSON_STRING Then Return TJSONString(val).Value Return Null EndMethod Method GetBoolean:Int( path:String) Local val:TJSONValue = Lookup( path) If val And val.Class = JSON_BOOLEAN Then Return TJSONBoolean(val).Value Return False EndMethod Method GetObject:TJSONObject( path:String) Local val:TJSONValue = Lookup( path) If val And val.Class = JSON_OBJECT Then Return TJSONObject(val) Return Null EndMethod Method GetArray:TJSONArray( path:String) Local val:TJSONValue = Lookup( path) If val And val.Class = JSON_ARRAY Then Return TJSONArray(val) Return Null EndMethod ' ' not realy sure if these GetArrayXXX are necessary ' Method GetArrayInt:Int[]( path:String) Local val:TJSONArray = GetArray( path) If val And (val.Items.Length > 0) Then Local a:Int[] = New Int[ val.Items.Length] For Local i:Int = 0 Until val.Items.Length Select val.Items[i].Class Case JSON_NUMBER a[i] = Int TJSONNumber( val.Items[i]).Value Case JSON_STRING a[i] = TJSONString( val.Items[i]).Value.ToInt() Case JSON_BOOLEAN a[i] = TJSONBoolean( val.Items[i]).Value EndSelect Next Return a EndIf Return Null EndMethod Method GetArrayDouble:Double[]( path:String) Local val:TJSONArray = GetArray( path) If val And (val.Items.Length > 0) Then Local a:Double[] = New Double[ val.Items.Length] For Local i:Int = 0 Until val.Items.Length Select val.Items[i].Class Case JSON_NUMBER a[i] = TJSONNumber( val.Items[i]).Value Case JSON_STRING a[i] = TJSONString( val.Items[i]).Value.ToDouble() Case JSON_BOOLEAN a[i] = Double TJSONBoolean( val.Items[i]).Value EndSelect Next Return a EndIf Return Null EndMethod Method GetArrayString:String[]( path:String) Local val:TJSONArray = GetArray( path) If val And (val.Items.Length > 0) Then Local a:String[] = New String[ val.Items.Length] For Local i:Int = 0 Until val.Items.Length Select val.Items[i].Class Case JSON_NUMBER, JSON_STRING, JSON_BOOLEAN, JSON_NULL a[i] = val.Items[i].ToString() Case JSON_OBJECT a[i] = "{}" Case JSON_ARRAY a[i] = "[]" EndSelect Next Return a EndIf Return Null EndMethod Method ToString:String() Return Root.ToString() EndMethod Method ToSource:String( level:Int = 0) Return Root.ToSource( level) EndMethod EndType ' 'MARK: Support Functions ' Private ' ' Simple token seperator ' Function GetNext:String( value:String Var, sep:String) If (value.Length <= 0) Or (sep.Length <= 0) Then Return Null Local res:String, index:Int = value.Find( sep) If index = 0 Then value = value[1..] Return Null ElseIf index >= 1 Then res = value[..index] value = value[ 1 + res.Length..] Return res EndIf res = value value = Null Return res EndFunction ' ' Checks if a string is a number ' Function IsNumber:Int( value:String) Const START:Int = Asc("0") Const STOP:Int = Asc("9") For Local i:Int = 0 Until value.Length Local c:Byte = value[i] If (c < START) Or (c > STOP) Then Return False Next Return True EndFunction ' ' Returns a "pretty" floating point number ' Function DoubleToString:String( value:Double) Const STR_FMT:String = "%f" Const CHAR_0:Byte = Asc("0") Const CHAR_DOT:Byte = Asc(".") Extern "C" Function modf_:Double( x:Double, iptr:Double Var) = "modf" Function snprintf_:Int( s:Byte Ptr, n:Int, Format$z, v1:Double) = "snprintf" EndExtern Local i:Double If modf_( value, i) = 0.0 Then Return String.FromLong( Long i) Else Local buf:Byte[32] Local sz:Int = snprintf_( buf, buf.Length, STR_FMT, value) sz :- 1 While (sz > 0) And (buf[ sz] = CHAR_0) If buf[ sz-1] = CHAR_DOT Then Exit sz :- 1 Wend sz :+ 1 If sz > 0 Then Return String.FromBytes( buf, sz) EndIf Return "0" EndFunction Function RepeatString:String( s:String, count:Int) Local res:String While count > 0 res :+ s count :- 1 Wend Return res EndFunction Public ' 'MARK: various test cases, each in its own Rem/EndRem block ' Rem Local array:TJSONValue = TJSONArray.Create( 4) array.SetByIndex( 0, TJSONString.Create( "string value")) array.SetByIndex( 1, TJSONNumber.Create( 1.5)) array.SetByIndex( 2, TJSONBoolean.Create( True)) array.SetByIndex( 3, TJSON.NIL) Local object_:TJSONValue = New TJSONObject object_.SetByName( "first", TJSONString.Create( "string value")) object_.SetByName( "second", TJSONNumber.Create( 1.5)) object_.SetByName( "third", TJSONBoolean.Create( True)) object_.SetByName( "fourth", TJSON.NIL) Local json:TJSON = TJSON.Create( New TJSONObject) json.Root.SetByName( "first", TJSONString.Create( "string value")) json.Root.SetByName( "second", TJSONNumber.Create( 1.5)) json.Root.SetByName( "third", TJSONBoolean.Create( True)) json.Root.SetByName( "fourth", TJSON.NIL) json.Root.SetByName( "array", array) json.Root.SetByName( "object", object_) Print json.ToSource() EndRem Rem Local jsop:TJSONParser = New TJSONParser Local json:TJSON = New TJSON jsop.Source = LoadString( "test.json") Print jsop.Source json.Root = jsop.Parse() If Not json.Root Then Print "~noops" End EndIf EndRem Rem Local json:TJSON = TJSON.Create( "[ 1,2,~q3.4 + 2~q,4,5 ]") Print "--------------------------------------------------------------------" Print json.ToSource() Print "--------------------------------------------------------------------" For Local s:String = EachIn json.GetArrayString("root") Print s Next EndRem Rem Const TEST_JSON:String = .. "{" +.. " first: ~qString value~q," +.. " second: 1.5," +.. " third: true," +.. " fourth: null," +.. " ~qthis is an array~q: [" +.. " ~qstring value~q," +.. " 1.5," +.. " true," +.. " null" +.. " ]," +.. " ~qthis is an object~q: {" +.. " first: ~qstring value~q," +.. " second: 1.5," +.. " third: true," +.. " fourth: null" +.. " }" +.. "}" 'Local json:TJSON = TJSON.Create( LoadString( "test.json")) Local json:TJSON = TJSON.Create( TEST_JSON) ' change some values 'json.SetValue( "fifth", New TJSONObject) 'json.SetValue( "this is an array.4", New TJSONObject) 'json.SetValue( "this is an object.fifth", New TJSONObject) Print "--------------------------------------------------------------------" Print json.ToString() Print "--------------------------------------------------------------------" Print json.ToSource() Print "--------------------------------------------------------------------" Print json.GetValue( "first").ToString() Print json.GetValue( "second").ToString() Print json.GetValue( "third").ToString() Print json.GetValue( "fourth").ToString() Print json.GetValue( "fifth").ToString() Print "--------------------------------------------------------------------" Print json.GetValue( "this is an array").ToString() Print "--------------------------------------------------------------------" Print json.GetValue( "this is an object").ToString() Print "--------------------------------------------------------------------" Print json.GetValue( "this is an array.0").ToString() Print json.GetValue( "this is an array.1").ToString() Print json.GetValue( "this is an array.2").ToString() Print json.GetValue( "this is an array.3").ToString() Print json.GetValue( "this is an array.4").ToString() Print "--------------------------------------------------------------------" Print json.GetValue( "this is an object.first").ToString() Print json.GetValue( "this is an object.second").ToString() Print json.GetValue( "this is an object.third").ToString() Print json.GetValue( "this is an object.fourth").ToString() Print json.GetValue( "this is an object.fifth").ToString() EndRem |
Comments
| ||
Oh Man! You beat me to it! I just wrote a class in C# that does this for .Net 1.1, and 2.0 projects, and thought I would do the same for BMAX. Way to go! It looks really great! |
| ||
Thanks, hope you find a use for it =) |
| ||
Nice work, reminds me of the SParse thing I did, albeit more flexible syntactically (not sure how the code is, don't plan on using it, just struck me as neat). |
| ||
grable, thank you so much! I will be using this in my game, Colosseum. This makes data handling so easy! I will also give you credit both in the source file itself (unchanged) and in my main menu. Again, thanks for writing this, you totally rock! My game is here, if you want to take a look. http://colosseum.devjavu.com JSON will be used in my next release, 0.2.7, to read and write all my game data and configurations, instead of what I was going to use (a homebrew bastardized .ini format which kind of sucked). |
| ||
Edited this great parser to allow negative numbers and scientific notated numbers as well: |
| ||
Just out of curiousity, can someone help me with this JSON problem I'm having with npm install for windows 8.1? I just tried npm install, and I get: This is my package.json file: Thank you for helping me! And EXCELLENT code, btw! :) Sincerely, ~GF |
Code Archives Forum