Code archives/File Utilities/Serialize/deserialize objects as JSON
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| Basic usage: serializer:jsonSerializer = New jsonSerializer.init() json:jsonValue = serializer.serializeObject( obj ) jsonstr:String = json.toString() deserializer:jsonDeserializer = New jsonDeserializer.init() parsedjson:jsonValue = jsonValue.fromString( jsonstr ) typeid:TTypeId = TTypeId.ForName( "MyType" ) obj:MyType = MyType( deserializer.deserializeObject( parsedjson, typeid ) ) See the code for a thorough example. It's possible to implement special handling for classes which inherit from Object. There are default handlers for String, TList, and TMap. For example, TList objects are serialized as json lists. It is possible to add your own handlers - see the init() method of jsonSerializer and jsonDeserializer for examples. Caveats: Arrays of more than one dimension are unsupported. Be careful about having cyclic references, the serializer is not equipped to handle them. Make good use of {json}, {nojson}, {jsonfld}, {nojsonfld}, {jsonbool}, and {jsoncont} metadata tags. See comments in the top of the source as well as the example code to understand usage. Requires this archive entry ("wild.bmx"): http://www.blitzmax.com/codearcs/codearcs.php?code=3176 | |||||
' --+-----------------------------------------------------------------------------------------+--
' | This code was originally written by Sophie Kirschner (meapineapple@gmail.com) and it is |
' | released as public domain. Please do not interpret that as liberty to claim credit that |
' | is not yours, or to sell this code when it could otherwise be obtained for free because |
' | that would be a really shitty thing of you to do. |
' --+-----------------------------------------------------------------------------------------+--
SuperStrict
Import brl.reflection
Import brl.linkedlist
Import brl.map
Import "wild.bmx" ' Dependency available here: http://www.blitzmax.com/codearcs/codearcs.php?code=3176
'Import brl.standardio ' Used by example program
' Usage: Field MyField {json} All other fields in this class without {json} metadata will not be serialized.
Const jsonSerializeMetadataInclude:String = "json"
' Usage: Field MyField {nojson} This field will not be serialized. This has no impact on other fields.
Const jsonSerializeMetadataExclude:String = "nojson"
' Usage: Field MyField {jsonbool} Compensates for BlitzMax's lack of a boolean primitive: Tag an integer to serialize it as a boolean.
Const jsonSerializeMetadataBoolean:String = "jsonbool"
' Usage: Field MyField {jsonfld="MyField2,MyField3"} Tag an object to serialize only the listed field(s). Supersedes the object's own tags.
Const jsonSerializeMetadataIncludeSub:String = "jsonfld"
' Usage: Field MyField {nojsonfld="MyField*"} Tag an object to exlude the listed fields from serialization.
Const jsonSerializeMetadataExcludeSub:String = "nojsonfld"
' Usage: Field MyField {jsoncont="MyType"} Tag a TList or TMap to inform the deserializer what class objects it contains should belong to.
Const jsonSerializeMetadataClassSub:String = "jsoncont"
' For info on what special wildcard characters are allowed in {jsonf} and {nojsonf} tags, please refer to "wild.bmx".
' This is stuff you really shouldn't be touching
Private
Global ListTypeId:TTypeId = TTypeId.ForName( "TList" )
Global MapTypeId:TTypeId = TTypeId.ForName( "TMap" )
Const jsonOpenBraceAsc:Int = Asc( "{" )
Const jsonCloseBraceAsc:Int = Asc( "}" )
Const jsonOpenBracketAsc:Int = Asc( "[" )
Const jsonCloseBracketAsc:Int = Asc( "]" )
Const jsonAssignmentAsc:Int = Asc( ":" )
Const jsonDelimiterAsc:Int = Asc( "," )
Const jsonDecimalAsc:Int = Asc( "." )
Const jsonStringAsc:Int = Asc( "~q" )
Const jsonEscapeAsc:Int = Asc( "\" )
Extern
Function bbRefFieldPtr:Byte Ptr( obj:Object, index:Int )
Function bbRefArrayElementPtr:Byte Ptr( sz:Int, array:Object, index:Int )
Function bbRefGetObject:Object( p:Byte Ptr )
Function bbRefGetObjectClass:Int( obj:Object )
Function bbRefGetSuperClass:Int( class:Int )
Function bbRefAssignObject( p:Byte Ptr, obj:Object )
End Extern
Public
' Example code
Rem
Type MyType
' Serialize a null reference
Field MyNull:Object = Null
' Serialize an integer
Field MyInt:Int = 8
' Serialize a boolean
Field MyBoolean:Int = True {jsonbool}
' Serialize a floating-point number
Field MyNumber:Double = 3.14
' Serialize a string
Field MyString:String = "hi"
' Serialize an array of strings
Field MyArray:String[] = [ "foo", "bar", "foobar" ]
' Serialize an object
Field MyObject:MySubType = New MySubType
' DON'T serialize this field
Field MyUnserializedVar:String = "don't serialize me!" {nojson}
' Create a list to be filled with data then serialized
Field MyList:TList = CreateList()
' Create a map to be filled with data then serialized
Field MyMap:TMap = CreateMap()
' Fill the list and map with data upon initialization
Method New()
MyList.addlast( "one" )
MyList.addlast( "two" )
MyList.addlast( "three" )
MyList.addlast( "four" )
MyMap.insert( "5", "five" )
MyMap.insert( "6", "six" )
MyMap.insert( "7", "seven" )
MyMap.insert( "8", "eight" )
MyMap.insert( "9,10,11", [ 9:Int, 10:Int, 11:Int ] )
End Method
End Type
Type MySubType
' Serialize a string
Field name:String = "Bob" {json}
' Serialize a long int
Field money:Long = 99999999 {json}
' Serialize a string
Field power:String = "lots" {json}
' Serialize an array of ints
Field favoriteNumbers:Int[] = [ 7, 9, 12 ] {json}
' DON'T serialize this field, because we're going to put a cyclic reference here
Field parent:MyType
' Serialize a list of objects, and inform the deserializer of the class of its contents using metadata
Field MyObjectList:TList = ListFromArray([ MyListEntry.Create("foo"), MyListEntry.Create("bar") ]) {jsoncont="MyListEntry"}
' Serialize this field differently to hand the cyclic reference: Include only the specified fields of the referenced object
Field selfReference1:MySubType = Self {jsonfld="name,money"}
' Serialize the same thing as above, except inversely defined
' (note it's not necessary to explicitly exclude the parent field, as the normal tags already do that)
Field selfReference2:MySubType = Self {nojsonfld="power,favoriteNumbers,MyObjectList,selfReference*"}
End Type
Type MyListEntry
Function Create:MyListEntry( name:String )
Local this:MyListEntry = New MyListEntry; this.name = name; Return this
End Function
Field name:String
End Type
' Create the object to be serialized
Local obj:MyType = New MyType
' Add a cyclic reference
obj.MyObject.parent = obj
' Finally, do the actual serialization
Print "Creating serializer"
Local serializer:jsonSerializer = New jsonSerializer.init()
Print "Serializing json"
Local json:jsonValue = serializer.serializeObject( obj )
Print "Generating string"
Local str:String = json.toString()
Print "Final json:"
Print str
' Now deserialze to get the original object back
Print "~nParsing output"
Local parsedjson:jsonValue = jsonValue.fromString( str )
Print "Creating deserializer"
Local deserializer:jsonDeserializer = New jsonDeserializer.init()
Print "Deserializing output"
Local newobj:MyType = MyType( deserializer.deserializeObject( parsedjson, TTypeId.ForName( "MyType" ) ) )
Print "Deserialized object's MyArray[2] field (should be ~qfoobar~q):"
Print newobj.MyArray[2]
EndRem
' Functions define special handling for types which inherit from Object
' If the absence of a handler function for serialization and deserialization a class is treated
' as a simple 1:1 correspondence between dict key,value pairs and class field,value pairs.
' Serialize string
Function jsonSerializeString:Object( obj:Object, member:TMember, controller:jsonSerializationController )
Return jsonValueString.Create( String( obj ) )
End Function
' Serialize linked list
Function jsonSerializeList:Object( obj:Object, member:TMember, controller:jsonSerializationController )
Local list:TList = TList( obj )
Local jsonList:jsonValueList = jsonValueList.Create()
For Local member:Object = EachIn list
jsonList.add( jsonSerializer( controller ).serializeObject( member ) )
Next
Return jsonList
End Function
' Serialize map
Function jsonSerializeMap:Object( obj:Object, member:TMember, controller:jsonSerializationController )
Local map:TMap = TMap( obj )
Local dict:jsonValueDict = jsonValueDict.Create()
For Local key:Object = EachIn map.Keys()
dict.add( key, jsonSerializer( controller ).serializeObject( map.ValueForKey( key ) ) )
Next
Return dict
End Function
' Deserialize string
Function jsonDeserializeString:Object( obj:Object, member:TMember, controller:jsonSerializationController )
Local value:jsonValueString = jsonValueString( obj )
If value
Return value.get()
Else
Return Null
EndIf
End Function
' Deserialize linked list
Function jsonDeserializeList:Object( obj:Object, member:TMember, controller:jsonSerializationController )
Local list:jsonValueList = jsonValueList( obj )
If list
Local retlist:TList = CreateList()
For Local value:jsonValue = EachIn list.get()
retlist.addlast( jsonDeserializer( controller ).deserializeObject( value, jsonDeserializer.jsonValueTypeId( value, member ) ) )
Next
Return retlist
Else
Return Null
EndIf
End Function
' Deserialize map
Function jsonDeserializeMap:Object( obj:Object, member:TMember, controller:jsonSerializationController )
Local dict:jsonValueDict = jsonValueDict( obj )
If dict
Local map:TMap = CreateMap()
For Local key:String = EachIn dict.get().Keys()
Local value:jsonValue = jsonValue( dict.get().ValueForKey( key ) )
map.insert( key, jsonDeserializer( controller ).deserializeObject( value, jsonDeserializer.jsonValueTypeId( value, member ) ) )
Next
Return map
Else
Return Null
EndIf
End Function
' jsonSerializer and jsonDeserializer both extend this class
Type jsonSerializationController
' Associates functions with TTypeId keys
Field typeHandlers:TMap = CreateMap()
' Add a new serializer/deserializer
Method addTypeHandler( typeid:TTypeId, typeHandlerFunc:Object( obj:Object, member:TMember, controller:jsonSerializationController ) )
typeHandlers.insert( typeid, jsonTypeHandlerFunc.Create( typeHandlerFunc ) )
End Method
End Type
' Container for serialization and deserializeation functions
Type jsonTypeHandlerFunc
' Reference to handler function
' obj - the jsonValue to serialize or Object to deserialize
' member - the TField where this value belongs
' controller - Reference to jsonSerializer or jsonDeserializer object
Field func:Object( obj:Object, member:TMember, controller:jsonSerializationController )
' Create a new handler
Function Create:jsonTypeHandlerFunc( func:Object( obj:Object, member:TMember, controller:jsonSerializationController ) )
Local this:jsonTypeHandlerFunc = New jsonTypeHandlerFunc; this.func = func; Return this
End Function
' Call handler function
Method handle:Object( obj:Object, member:TMember, controller:jsonSerializationController )
Return func( obj, member, controller )
End Method
End Type
' Recursively turn an arbitrary object and its fields into jsonValue objects which can be encoded as a string
Type jsonSerializer Extends jsonSerializationController
' Initialize with standard serializers (String, TList, and TMap)
' Easy to add your own handlers upon calling init. Example call:
' json.init( [ MyFirstTypeId, MySecondTypeId ], [ jsonSerialize1Type, jsonSerialize2Type ] )
Method init:jsonSerializer( ..
moreHandlersTypeId:TTypeId[] = Null, ..
moreHandlersFunc:Object( obj:Object, member:TMember, controller:jsonSerializationController )[] = Null ..
)
addTypeHandler( StringTypeId, jsonSerializeString )
addTypeHandler( ListTypeId, jsonSerializeList )
addTypeHandler( MapTypeId, jsonSerializeMap )
If moreHandlersTypeId And moreHandlersFunc
For Local i:Int = 0 Until moreHandlersTypeId.length
addTypeHandler( moreHandlersTypeId[i], moreHandlersFunc[i] )
Next
EndIf
Return Self
End Method
' Serialize an object
Method serializeObject:jsonValue( obj:Object, parentField:TField = Null )
If obj = Null
Return jsonValueNull.Create()
Else
Local typeid:TTypeId = TTypeId.ForObject( obj )
If typeid.ElementType()
Return jsonSerialize1DArray( obj, Self )
ElseIf typeHandlers.Contains( typeid )
Local handler:jsonTypeHandlerFunc = jsonTypeHandlerFunc( typeHandlers.ValueForKey( typeid ) )
Return jsonValue( handler.handle( obj, parentField, Self ) )
Else
' look for {jsonf} and {nojsonf} tags belonging to parent field
Local jsonf:String[] = Null
Local nojsonf:String[] = Null
If parentField
Local jsonfMeta:String = parentField.MetaData( jsonSerializeMetadataIncludeSub )
Local nojsonfMeta:String = parentField.MetaData( jsonSerializeMetadataExcludeSub )
If jsonfMeta
jsonf = jsonfMeta.split(",")
ElseIf nojsonfMeta
nojsonf = nojsonfMeta.split(",")
EndIf
EndIf
' look for {json} tags
Local serializeMetadataOnly:Int = False
For Local member:TField = EachIn typeid.EnumFields()
If member.MetaData( jsonSerializeMetadataInclude )
serializeMetadataOnly = True
Exit
EndIf
Next
' build the dict
Local dict:jsonValueDict = jsonValueDict.Create()
For Local member:TField = EachIn typeid.EnumFields()
Local add:Int = False
If nojsonf And metaContains( nojsonf, member.Name() )
add = False
ElseIf jsonf
add = metaContains( jsonf, member.Name() )
ElseIf member.MetaData( jsonSerializeMetadataIncludeSub ) Or ..
member.MetaData( jsonSerializeMetadataExcludeSub ) Or ..
member.MetaData( jsonSerializeMetadataBoolean ) Or ..
member.MetaData( jsonSerializeMetadataClassSub )
add = True
Else
' {json}
Local incl:Int = ( (Not serializeMetadataOnly) Or member.MetaData( jsonSerializeMetadataInclude ) )
' {nojson}
Local excl:Int = member.MetaData( jsonSerializeMetadataExclude ) <> Null
' result
add = incl And Not excl
EndIf
If add dict.add( member.Name(), serializeField( obj, member ) )
Next
Return dict
EndIf
EndIf
End Method
Method serializeField:jsonValue( obj:Object, member:TField )
Local id:TTypeId = member.TypeId()
Local p:Byte Ptr = bbRefFieldPtr( obj, member._index )
If id = ByteTypeId
Return jsonValueInt.Create( (Byte Ptr p)[0] )
ElseIf id = ShortTypeId
Return jsonValueInt.Create( (Short Ptr p)[0] )
ElseIf id = IntTypeId
If member.MetaData( jsonSerializeMetadataBoolean )
Return jsonValueBool.Create( (Int Ptr p)[0] )
Else
Return jsonValueInt.Create( (Int Ptr p)[0] )
EndIf
ElseIf id = LongTypeId
Return jsonValueInt.Create( (Long Ptr p)[0] )
ElseIf id = FloatTypeId
Return jsonValueFloat.Create( (Float Ptr p)[0] )
ElseIf id = DoubleTypeId
Return jsonValueFloat.Create( (Double Ptr p)[0] )
Else
Return serializeObject( bbRefGetObject( p ), member )
EndIf
End Method
Function metaContains:Int( array:String[], sub:String )
For Local str:String = EachIn array
If matchWild( str, sub ) Return True
Next
Return False
End Function
' Make jsonValueList from 1D array
Function jsonSerialize1DArray:jsonValueList( obj:Object, controller:jsonSerializationController )
Local typeid:TTypeId = TTypeId.ForObject( obj )
Local elementid:TTypeId = typeid.ElementType()
Local list:jsonValueList = jsonValueList.Create()
If elementid = ByteTypeId
Local array:Byte[] = Byte[] obj
For Local member:Int = 0 Until array.length
list.add( jsonValueInt.Create( array[ member ] ) )
Next
ElseIf elementid = ShortTypeId
Local array:Short[] = Short[] obj
For Local member:Int = 0 Until array.length
list.add( jsonValueInt.Create( array[ member ] ) )
Next
ElseIf elementid = IntTypeId
Local array:Int[] = Int[] obj
For Local member:Int = 0 Until array.length
list.add( jsonValueInt.Create( array[ member ] ) )
Next
ElseIf elementid = LongTypeId
Local array:Long[] = Long[] obj
For Local member:Int = 0 Until array.length
list.add( jsonValueInt.Create( array[ member ] ) )
Next
ElseIf elementid = FloatTypeId
Local array:Float[] = Float[] obj
For Local member:Int = 0 Until array.length
list.add( jsonValueFloat.Create( array[ member ] ) )
Next
ElseIf elementid = DoubleTypeId
Local array:Double[] = Double[] obj
For Local member:Int = 0 Until array.length
list.add( jsonValueFloat.Create( array[ member ] ) )
Next
ElseIf elementid = StringTypeId
Local array:String[] = String[] obj
For Local member:Int = 0 Until array.length
list.add( jsonValueString.Create( array[ member ] ) )
Next
Else
Local array:Object[] = Object[] obj
For Local member:Int = 0 Until array.length
list.add( jsonSerializer( controller ).serializeObject( array[ member ] ) )
Next
EndIf
Return list
End Function
End Type
' Recusively turn jsonValue objects, which can be decoded from a string, into an arbitrary class with the specified fields
Type jsonDeserializer Extends jsonSerializationController
' Initialize with standard deserializers (String, TList, and TMap)
' Easy to add your own handlers upon calling init. Example call:
' json.init( [ MyFirstTypeId, MySecondTypeId ], [ jsonDeserialize1Type, jsonDeserialize2Type ] )
Method init:jsonDeserializer( ..
moreHandlersTypeId:TTypeId[] = Null, ..
moreHandlersFunc:Object( obj:Object, member:TMember, controller:jsonSerializationController )[] = Null ..
)
addTypeHandler( StringTypeId, jsonDeserializeString )
addTypeHandler( ListTypeId, jsonDeserializeList )
addTypeHandler( MapTypeId, jsonDeserializeMap )
If moreHandlersTypeId And moreHandlersFunc
For Local i:Int = 0 Until moreHandlersTypeId.length
addTypeHandler( moreHandlersTypeId[i], moreHandlersFunc[i] )
Next
EndIf
Return Self
End Method
' Deserialize an object
Method deserializeObject:Object( json:jsonValue, typeid:TTypeId, parentField:TField = Null )
If jsonValueNull( json )
Return Null
ElseIf typeHandlers.Contains( typeid )
Local handler:jsonTypeHandlerFunc = jsonTypeHandlerFunc( typeHandlers.ValueForKey( typeid ) )
Return handler.handle( json, parentField, Self )
Else
Local obj:Object = typeid.NewObject()
Local dict:jsonValueDict = jsonValueDict( json )
For Local member:TField = EachIn typeid.EnumFields()
Local value:jsonValue = jsonValue( dict.get().ValueForKey( member.Name() ) )
If value
Local p:Byte Ptr = bbRefFieldPtr( obj, member._index )
Local id:TTypeId = member.TypeId()
deserializeMember( value, p, id, member )
EndIf
Next
Return obj
EndIf
End Method
Method deserializeMember( value:jsonValue, p:Byte Ptr, typeid:TTypeId, parentField:TField )
If typeid.ElementType()
Local list:jsonValueList = jsonValueList( value )
If list
Local arrayLength:Int = list.get().count()
Local arrayType:TTypeId = typeid.ElementType()
Local array:Object = typeid.NewArray( arrayLength )
Local arrayIndex:Int = 0
For Local element:jsonValue = EachIn list.get()
Local p:Byte Ptr = bbRefArrayElementPtr( typeid.ElementType()._size, array, arrayIndex )
deserializeMember( element, p, arrayType, parentField )
arrayIndex :+ 1
Next
Else
Local array:Object = typeid.NewArray( 0 )
assignObject( p, array )
EndIf
ElseIf typeid = ByteTypeId
Local number:Byte = 0
If jsonValueBool( value )
number = jsonValueBool( value ).get()
ElseIf jsonValueInt( value )
number = jsonValueInt( value ).get()
ElseIf jsonValueFloat( value )
number = jsonValueFloat( value ).get()
ElseIf jsonValueString( value )
number = Byte jsonValueString( value ).get()
EndIf
(Byte Ptr p)[0] = number
ElseIf typeid = ShortTypeId
Local number:Short = 0
If jsonValueBool( value )
number = jsonValueBool( value ).get()
ElseIf jsonValueInt( value )
number = jsonValueInt( value ).get()
ElseIf jsonValueFloat( value )
number = jsonValueFloat( value ).get()
ElseIf jsonValueString( value )
number = Short jsonValueString( value ).get()
EndIf
(Short Ptr p)[0] = number
ElseIf typeid = IntTypeId
Local number:Int = 0
If jsonValueBool( value )
number = jsonValueBool( value ).get()
ElseIf jsonValueInt( value )
number = jsonValueInt( value ).get()
ElseIf jsonValueFloat( value )
number = jsonValueFloat( value ).get()
ElseIf jsonValueString( value )
number = Int jsonValueString( value ).get()
EndIf
(Int Ptr p)[0] = number
ElseIf typeid = LongTypeId
Local number:Long = 0
If jsonValueBool( value )
number = jsonValueBool( value ).get()
ElseIf jsonValueInt( value )
number = jsonValueInt( value ).get()
ElseIf jsonValueFloat( value )
number = jsonValueFloat( value ).get()
ElseIf jsonValueString( value )
number = Long jsonValueString( value ).get()
EndIf
(Long Ptr p)[0] = number
ElseIf typeid = FloatTypeId
Local number:Float = 0
If jsonValueBool( value )
number = jsonValueBool( value ).get()
ElseIf jsonValueInt( value )
number = jsonValueInt( value ).get()
ElseIf jsonValueFloat( value )
number = jsonValueFloat( value ).get()
ElseIf jsonValueString( value )
number = Float jsonValueString( value ).get()
EndIf
(Float Ptr p)[0] = number
ElseIf typeid = DoubleTypeId
Local number:Double = 0
If jsonValueBool( value )
number = jsonValueBool( value ).get()
ElseIf jsonValueInt( value )
number = jsonValueInt( value ).get()
ElseIf jsonValueFloat( value )
number = jsonValueFloat( value ).get()
ElseIf jsonValueString( value )
number = Double jsonValueString( value ).get()
EndIf
(Double Ptr p)[0] = number
Else
assignObject( p, deserializeObject( value, typeid, parentField ) )
EndIf
End Method
Function assignObject( p:Byte Ptr, value:Object )
If value
Local id:TTypeId = TTypeId.ForObject( value )
Local class:Int = bbRefGetObjectClass( value )
While class And class <> id._class
class = bbRefGetSuperClass( class )
Wend
EndIf
bbRefAssignObject( p, value )
End Function
' Determine TypeId that jsonValue should deserialize to when bmax doesn't explitly specify
' (e.g. objects within a list)
Function jsonValueTypeId:TTypeId( value:jsonValue, member:TMember )
Local jsonc:String
If member jsonc = member.MetaData( jsonSerializeMetadataClassSub )
If jsonc
Return TTypeId.ForName( jsonc )
ElseIf jsonValueString( value )
Return StringTypeId
ElseIf jsonValueList( value )
Return ListTypeId
ElseIf jsonValueDict( value )
Return MapTypeId
Else
Return ObjectTypeId
EndIf
End Function
End Type
' Base json value type
Type jsonValue
' Get json string from object
Method toString:String()
Return Null
End Method
' Get object from json string
Function fromString:jsonValue( str:String, low:Int = 0, high:Int = 0 )
If high = 0 high = str.length
' Get next occurence of character in same scope, considering quotes, brackets, etc.
Function nextChar:Int( str:String, char:Int, low:Int, high:Int )
Local nestBrace:Int = 0, nestBracket:Int = 0, inQuote:Int = False
Local i:Int = low
While i < high
If nestBrace = 0 And nestBracket = 0 And inQuote = False And str[i] = char
Return i
ElseIf str[i] = jsonEscapeAsc
i :+ 1
ElseIf str[i] = jsonStringAsc
inQuote = Not inQuote
ElseIf str[i] = jsonOpenBraceAsc
nestBrace :+ 1
ElseIf str[i] = jsonOpenBracketAsc
nestBracket :+ 1
ElseIf str[i] = jsonCloseBraceAsc
nestBrace :- 1
ElseIf str[i] = jsonCloseBracketAsc
nestBracket :- 1
EndIf
i :+ 1
Wend
Return -1
End Function
' Skip whitespace
While str[ low ] < 32
low :+ 1
Wend
' Parse string
If str[ low ] = jsonStringAsc
Return jsonValueString.Create( jsonParseString( str, low, high ) )
' Parse dict
ElseIf str[ low ] = jsonOpenBraceAsc
Local dict:jsonValueDict = jsonValueDict.Create()
Local i:Int = low+1
While i < high-1
Local assign:Int = nextChar( str, jsonAssignmentAsc, i, high-1 )
Local delim:Int = nextChar( str, jsonDelimiterAsc, i, high-1 )
Assert assign >= 0, "Syntax error: Missing assignment character"
Local key:String = jsonParseString( str, i, assign )
If delim >= 0
Local value:jsonValue = jsonValue.fromString( str, assign+1, delim )
dict.add( key, value )
i = delim+1
Else
Local value:jsonValue = jsonValue.fromString( str, assign+1, high-1 )
dict.add( key, value )
Exit
EndIf
Wend
Return dict
' Parse list
ElseIf str[ low ] = jsonOpenBracketAsc
Local list:jsonValueList = jsonValueList.Create()
Local i:Int = low+1
While i < high-1
Local delim:Int = nextChar( str, jsonDelimiterAsc, i, high-1 )
If delim >= 0
list.add( jsonValue.fromString( str, i, delim ) )
i = delim+1
Else
list.add( jsonValue.fromString( str, i, high-1 ) )
Exit
EndIf
Wend
Return list
' Parse others
Else
Local sub:String = str[ low..high ]
' Parse number
If str[ low ] >= 48 And str[ low ] <= 57
Local isDecimal:Int = False
For Local i:Int = low Until high
If str[i] = jsonDecimalAsc isDecimal = True; Exit
Next
If isDecimal
Return jsonValueFloat.Create( Double( sub ) )
Else
Return jsonValueInt.Create( Long( sub ) )
EndIf
' Parse keywords
ElseIf sub = "null"
Return jsonValueNull.Create()
ElseIf sub = "true"
Return jsonValueBool.Create( True )
ElseIf sub = "false"
Return jsonValueBool.Create( False )
EndIf
EndIf
Throw "Syntax error: Unexpected character "+Chr( str[ low ] )
End Function
' Utility functions for strings
Function jsonSanitizeString:String( str:String )
Return str.Replace( "~q", "\~q" )
End Function
Function jsonDesanitizeString:String( str:String )
Return str.Replace( "\~q", "~q" )
End Function
Function jsonParseString:String( str:String, low:Int, high:Int )
Local sub:String = str[ low..high ]
sub = sub.Trim()
Local qstart:Int = (sub[0] = jsonStringAsc)
Local qend:Int = (sub[ sub.length-1 ] = jsonStringAsc) And (sub.length < 2 Or sub[ sub.length-2 ] <> jsonEscapeAsc)
If qstart Or qend sub = sub[ qstart .. sub.length-qend ]
Return jsonDesanitizeString( sub )
End Function
End Type
' Null type
Type jsonValueNull Extends jsonValue
Function Create:jsonValueNull()
Return New jsonValueNull
End Function
Method toString:String()
Return "null"
End Method
Method get:Object()
Return Null
End Method
End Type
' Boolean type
Type jsonValueBool Extends jsonValue
Field value:Int
Function Create:jsonValueBool( value:Int = 0:Int )
Local this:jsonValueBool = New jsonValueBool; this.value = value; Return this
End Function
Method toString:String()
If value Return "true" Else Return "false"
End Method
Method get:Int()
Return value
End Method
Method set( value:Int )
Self.value = value
End Method
End Type
' Integer type
Type jsonValueInt Extends jsonValue
Field value:Long
Function Create:jsonValueInt( value:Long = 0:Long )
Local this:jsonValueInt = New jsonValueInt; this.value = value; Return this
End Function
Method toString:String()
Return String( value )
End Method
Method get:Long()
Return value
End Method
Method set( value:Long )
Self.value = value
End Method
End Type
' Float type
Type jsonValueFloat Extends jsonValue
Field value:Double
Function Create:jsonValueFloat( value:Double = 0:Double )
Local this:jsonValueFloat = New jsonValueFloat; this.value = value; Return this
End Function
Method toString:String()
Return String( value )
End Method
Method get:Double()
Return value
End Method
Method set( value:Double )
Self.value = value
End Method
End Type
' String type
Type jsonValueString Extends jsonValue
Field value:String
Function Create:jsonValueString( value:String = "" )
Local this:jsonValueString = New jsonValueString; this.value = value; Return this
End Function
Method toString:String()
Return "~q" + jsonSanitizeString( value ) + "~q"
End Method
Method get:String()
Return value
End Method
Method set( value:String )
Self.value = value
End Method
End Type
' List type
Type jsonValueList Extends jsonValue
Field value:TList
Function Create:jsonValueList( value:TList = Null )
If value = Null value = CreateList()
Local this:jsonValueList = New jsonValueList; this.value = value; Return this
End Function
Method toString:String()
Local content:String = "", first:Int = True
For Local member:jsonValue = EachIn value
If first first = False Else content :+ ","
content :+ member.toString()
Next
Return "[" + content + "]"
End Method
Method get:TList()
Return value
End Method
Method set( value:TList )
Self.value = value
End Method
Method add:TLink( value:jsonValue )
Return Self.value.addlast( value )
End Method
End Type
' Dict type
Type jsonValueDict Extends jsonValue
Field value:TMap
Function Create:jsonValueDict( value:TMap = Null )
If value = Null value = CreateMap()
Local this:jsonValueDict = New jsonValueDict; this.value = value; Return this
End Function
Method toString:String()
Local content:String = "", first:Int = True
For Local key:Object = EachIn value.Keys()
If first first = False Else content :+ ","
Local member:Object = value.ValueForKey( key )
content :+ "~q" + jsonSanitizeString( String( key ) ) + "~q:" + jsonValue( member ).toString()
Next
Return "{" + content + "}"
End Method
Method get:TMap()
Return value
End Method
Method set( value:TMap )
Self.value = value
End Method
Method add( key:Object, value:jsonValue )
Self.value.insert( key, value )
End Method
End Type |
Comments
None.
Code Archives Forum