March 01, 2021, 10:15:02 PM

Author Topic: [bmx] Serialize/deserialize objects as JSON by Pineapple [ 1+ years ago ]  (Read 614 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
Title : Serialize/deserialize objects as JSON
Author : Pineapple
Posted : 1+ years ago

Description : 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"): <a href="http://www.blitzmax.com/codearcs/codearcs.php?code=3176" target="_blank">http://www.blitzmax.com/codearcs/codearcs.php?code=3176[/url]


Code :
Code: BlitzMax
  1. '   --+-----------------------------------------------------------------------------------------+--
  2. '     | This code was originally written by Sophie Kirschner (meapineapple@gmail.com) and it is |  
  3. '     | released as public domain. Please do not interpret that as liberty to claim credit that |  
  4. '     | is not yours, or to sell this code when it could otherwise be obtained for free because |  
  5. '     |                    that would be a really shitty thing of you to do.                    |
  6. '   --+-----------------------------------------------------------------------------------------+--
  7.  
  8.  
  9.  
  10. SuperStrict
  11.  
  12. Import brl.reflection
  13. Import brl.linkedlist
  14. Import brl.map
  15. Import "wild.bmx"       ' Dependency available here: http://www.blitzmax.com/codearcs/codearcs.php?code=3176
  16. 'Import brl.standardio   ' Used by example program
  17.  
  18.  
  19.  
  20. ' Usage: Field MyField {json}                                        All other fields in this class without {json} metadata will not be serialized.
  21. Const jsonSerializeMetadataInclude:String = "json"
  22.  
  23. ' Usage: Field MyField {nojson}                                              This field will not be serialized. This has no impact on other fields.
  24. Const jsonSerializeMetadataExclude:String = "nojson"
  25.  
  26. ' Usage: Field MyField {jsonbool}              Compensates for BlitzMax's lack of a boolean primitive: Tag an integer to serialize it as a boolean.
  27. Const jsonSerializeMetadataBoolean:String = "jsonbool"
  28.  
  29. ' Usage: Field MyField {jsonfld="MyField2,MyField3"}         Tag an object to serialize only the listed field(s). Supersedes the object's own tags.
  30. Const jsonSerializeMetadataIncludeSub:String = "jsonfld"
  31.  
  32. ' Usage: Field MyField {nojsonfld="MyField*"}                                         Tag an object to exlude the listed fields from serialization.
  33. Const jsonSerializeMetadataExcludeSub:String = "nojsonfld"
  34.  
  35. ' Usage: Field MyField {jsoncont="MyType"}          Tag a TList or TMap to inform the deserializer what class objects it contains should belong to.
  36. Const jsonSerializeMetadataClassSub:String = "jsoncont"
  37.  
  38. ' For info on what special wildcard characters are allowed in {jsonf} and {nojsonf} tags, please refer to "wild.bmx".
  39.  
  40.  
  41.  
  42. ' This is stuff you really shouldn't be touching
  43. Private
  44.  
  45. Global ListTypeId:TTypeId = TTypeId.ForName( "TList" )
  46. Global MapTypeId:TTypeId = TTypeId.ForName( "TMap" )
  47.  
  48. Const jsonOpenBraceAsc:Int = Asc( "{" )
  49. Const jsonCloseBraceAsc:Int = Asc( "}" )
  50. Const jsonOpenBracketAsc:Int = Asc( "[" )
  51. Const jsonCloseBracketAsc:Int = Asc( "]" )
  52. Const jsonAssignmentAsc:Int = Asc( ":" )
  53. Const jsonDelimiterAsc:Int = Asc( "," )
  54. Const jsonDecimalAsc:Int = Asc( "." )
  55. Const jsonStringAsc:Int = Asc( "~q" )
  56. Const jsonEscapeAsc:Int = Asc( "" )
  57.  
  58. Extern
  59. Function bbRefFieldPtr:Byte Ptr( obj:Object, index:Int )
  60. Function bbRefArrayElementPtr:Byte Ptr( sz:Int, array:Object, index:Int )
  61. Function bbRefGetObject:Object( p:Byte Ptr )
  62. Function bbRefGetObjectClass:Int( obj:Object )
  63. Function bbRefGetSuperClass:Int( class:Int )
  64. Function bbRefAssignObject( p:Byte Ptr, obj:Object )
  65. End Extern
  66.  
  67. Public
  68.  
  69.  
  70.  
  71. ' Example code
  72.  
  73. Rem
  74.  
  75. Type MyType
  76.  
  77.     ' Serialize a null reference
  78.     Field MyNull:Object = Null
  79.    
  80.     ' Serialize an integer
  81.     Field MyInt:Int = 8
  82.    
  83.     ' Serialize a boolean
  84.     Field MyBoolean:Int = True {jsonbool}
  85.    
  86.     ' Serialize a floating-point number
  87.     Field MyNumber:Double = 3.14
  88.    
  89.     ' Serialize a string
  90.     Field MyString:String = "hi"
  91.    
  92.     ' Serialize an array of strings
  93.     Field MyArray:String[] = [ "foo", "bar", "foobar" ]
  94.    
  95.     ' Serialize an object
  96.     Field MyObject:MySubType = New MySubType
  97.    
  98.     ' DON'T serialize this field
  99.     Field MyUnserializedVar:String = "don't serialize me!" {nojson}
  100.    
  101.     ' Create a list to be filled with data then serialized
  102.     Field MyList:TList = CreateList()
  103.    
  104.     ' Create a map to be filled with data then serialized
  105.     Field MyMap:TMap = CreateMap()
  106.    
  107.     ' Fill the list and map with data upon initialization
  108.     Method New()
  109.         MyList.addlast( "one" )
  110.         MyList.addlast( "two" )
  111.         MyList.addlast( "three" )
  112.         MyList.addlast( "four" )
  113.         MyMap.insert( "5", "five" )
  114.         MyMap.insert( "6", "six" )
  115.         MyMap.insert( "7", "seven" )
  116.         MyMap.insert( "8", "eight" )
  117.         MyMap.insert( "9,10,11", [ 9:Int, 10:Int, 11:Int ] )
  118.     End Method
  119.    
  120. End Type
  121.  
  122. Type MySubType
  123.  
  124.     ' Serialize a string
  125.     Field name:String = "Bob" {json}
  126.    
  127.     ' Serialize a long int
  128.     Field money:Long = 99999999 {json}
  129.    
  130.     ' Serialize a string
  131.     Field power:String = "lots" {json}
  132.    
  133.     ' Serialize an array of ints
  134.     Field favoriteNumbers:Int[] = [ 7, 9, 12 ] {json}
  135.    
  136.     ' DON'T serialize this field, because we're going to put a cyclic reference here
  137.     Field parent:MyType
  138.    
  139.     ' Serialize a list of objects, and inform the deserializer of the class of its contents using metadata
  140.     Field MyObjectList:TList = ListFromArray([ MyListEntry.Create("foo"), MyListEntry.Create("bar") ]) {jsoncont="MyListEntry"}
  141.    
  142.     ' Serialize this field differently to hand the cyclic reference: Include only the specified fields of the referenced object
  143.     Field selfReference1:MySubType = Self {jsonfld="name,money"}
  144.    
  145.     ' Serialize the same thing as above, except inversely defined
  146.     ' (note it's not necessary to explicitly exclude the parent field, as the normal tags already do that)
  147.     Field selfReference2:MySubType = Self {nojsonfld="power,favoriteNumbers,MyObjectList,selfReference*"}
  148.    
  149. End Type
  150.  
  151. Type MyListEntry
  152.  
  153.     Function Create:MyListEntry( name:String )
  154.         Local this:MyListEntry = New MyListEntry; this.name = name; Return this
  155.     End Function
  156.  
  157.     Field name:String
  158.    
  159. End Type
  160.  
  161. ' Create the object to be serialized
  162. Local obj:MyType = New MyType
  163. ' Add a cyclic reference
  164. obj.MyObject.parent = obj
  165.  
  166. ' Finally, do the actual serialization
  167. Print "Creating serializer"
  168. Local serializer:jsonSerializer = New jsonSerializer.init()
  169. Print "Serializing json"
  170. Local json:jsonValue = serializer.serializeObject( obj )
  171. Print "Generating string"
  172. Local str:String = json.toString()
  173. Print "Final json:"
  174. Print str
  175.  
  176. ' Now deserialze to get the original object back
  177. Print "~nParsing output"
  178. Local parsedjson:jsonValue = jsonValue.fromString( str )
  179. Print "Creating deserializer"
  180. Local deserializer:jsonDeserializer = New jsonDeserializer.init()
  181. Print "Deserializing output"
  182. Local newobj:MyType = MyType( deserializer.deserializeObject( parsedjson, TTypeId.ForName( "MyType" ) ) )
  183. Print "Deserialized object's MyArray[2] field (should be ~qfoobar~q):"
  184. Print newobj.MyArray[2]
  185.  
  186.  
  187. EndRem
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194. ' Functions define special handling for types which inherit from Object
  195. ' If the absence of a handler function for serialization and deserialization a class is treated
  196. ' as a simple 1:1 correspondence between dict key,value pairs and class field,value pairs.
  197.  
  198. ' Serialize string
  199. Function jsonSerializeString:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  200.     Return jsonValueString.Create( String( obj ) )
  201. End Function
  202.  
  203. ' Serialize linked list
  204. Function jsonSerializeList:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  205.     Local list:TList = TList( obj )
  206.     Local jsonList:jsonValueList = jsonValueList.Create()
  207.     For Local member:Object = EachIn list
  208.         jsonList.add( jsonSerializer( controller ).serializeObject( member ) )
  209.     Next
  210.     Return jsonList
  211. End Function
  212.  
  213. ' Serialize map
  214. Function jsonSerializeMap:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  215.     Local map:TMap = TMap( obj )
  216.     Local dict:jsonValueDict = jsonValueDict.Create()
  217.     For Local key:Object = EachIn map.Keys()
  218.         dict.add( key, jsonSerializer( controller ).serializeObject( map.ValueForKey( key ) ) )
  219.     Next
  220.     Return dict
  221. End Function
  222.  
  223. ' Deserialize string
  224. Function jsonDeserializeString:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  225.     Local value:jsonValueString = jsonValueString( obj )
  226.     If value
  227.         Return value.get()
  228.     Else
  229.         Return Null
  230.     EndIf
  231. End Function
  232.  
  233. ' Deserialize linked list
  234. Function jsonDeserializeList:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  235.     Local list:jsonValueList = jsonValueList( obj )
  236.     If list
  237.         Local retlist:TList = CreateList()
  238.         For Local value:jsonValue = EachIn list.get()
  239.             retlist.addlast( jsonDeserializer( controller ).deserializeObject( value, jsonDeserializer.jsonValueTypeId( value, member ) ) )
  240.         Next
  241.         Return retlist
  242.     Else
  243.         Return Null
  244.     EndIf
  245. End Function
  246.  
  247. ' Deserialize map
  248. Function jsonDeserializeMap:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  249.     Local dict:jsonValueDict = jsonValueDict( obj )
  250.     If dict
  251.         Local map:TMap = CreateMap()
  252.         For Local key:String = EachIn dict.get().Keys()
  253.             Local value:jsonValue = jsonValue( dict.get().ValueForKey( key ) )
  254.             map.insert( key, jsonDeserializer( controller ).deserializeObject( value, jsonDeserializer.jsonValueTypeId( value, member ) ) )
  255.         Next
  256.         Return map
  257.     Else
  258.         Return Null
  259.     EndIf
  260. End Function
  261.  
  262.  
  263.  
  264.  
  265.  
  266. ' jsonSerializer and jsonDeserializer both extend this class
  267. Type jsonSerializationController
  268.  
  269.     ' Associates functions with TTypeId keys
  270.     Field typeHandlers:TMap = CreateMap()
  271.    
  272.     ' Add a new serializer/deserializer
  273.     Method addTypeHandler( typeid:TTypeId, typeHandlerFunc:Object( obj:Object, member:TMember, controller:jsonSerializationController ) )
  274.         typeHandlers.insert( typeid, jsonTypeHandlerFunc.Create( typeHandlerFunc ) )
  275.     End Method
  276.  
  277. End Type
  278.  
  279. ' Container for serialization and deserializeation functions
  280. Type jsonTypeHandlerFunc
  281.     ' Reference to handler function
  282.     ' obj - the jsonValue to serialize or Object to deserialize
  283.     ' member - the TField where this value belongs
  284.     ' controller - Reference to jsonSerializer or jsonDeserializer object
  285.     Field func:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  286.     ' Create a new handler
  287.     Function Create:jsonTypeHandlerFunc( func:Object( obj:Object, member:TMember, controller:jsonSerializationController ) )
  288.         Local this:jsonTypeHandlerFunc = New jsonTypeHandlerFunc; this.func = func; Return this
  289.     End Function
  290.     ' Call handler function
  291.     Method handle:Object( obj:Object, member:TMember, controller:jsonSerializationController )
  292.         Return func( obj, member, controller )
  293.     End Method
  294. End Type
  295.  
  296. ' Recursively turn an arbitrary object and its fields into jsonValue objects which can be encoded as a string
  297. Type jsonSerializer Extends jsonSerializationController
  298.    
  299.     ' Initialize with standard serializers (String, TList, and TMap)
  300.     ' Easy to add your own handlers upon calling init. Example call:
  301.     ' json.init( [ MyFirstTypeId, MySecondTypeId ], [ jsonSerialize1Type, jsonSerialize2Type ] )
  302.     Method init:jsonSerializer( ..
  303.         moreHandlersTypeId:TTypeId[] = Null, ..
  304.         moreHandlersFunc:Object( obj:Object, member:TMember, controller:jsonSerializationController )[] = Null ..
  305.     )
  306.         addTypeHandler( StringTypeId, jsonSerializeString )
  307.         addTypeHandler( ListTypeId, jsonSerializeList )
  308.         addTypeHandler( MapTypeId, jsonSerializeMap )
  309.         If moreHandlersTypeId And moreHandlersFunc
  310.             For Local i:Int = 0 Until moreHandlersTypeId.length
  311.                 addTypeHandler( moreHandlersTypeId[i], moreHandlersFunc[i] )
  312.             Next
  313.         EndIf
  314.         Return Self
  315.     End Method
  316.    
  317.     ' Serialize an object
  318.     Method serializeObject:jsonValue( obj:Object, parentField:TField = Null )
  319.         If obj = Null
  320.             Return jsonValueNull.Create()
  321.         Else
  322.             Local typeid:TTypeId = TTypeId.ForObject( obj )
  323.             If typeid.ElementType()
  324.                 Return jsonSerialize1DArray( obj, Self )
  325.             ElseIf typeHandlers.Contains( typeid )
  326.                 Local handler:jsonTypeHandlerFunc = jsonTypeHandlerFunc( typeHandlers.ValueForKey( typeid ) )
  327.                 Return jsonValue( handler.handle( obj, parentField, Self ) )
  328.             Else
  329.                 ' look for {jsonf} and {nojsonf} tags belonging to parent field
  330.                 Local jsonf:String[] = Null
  331.                 Local nojsonf:String[] = Null
  332.                 If parentField
  333.                     Local jsonfMeta:String = parentField.MetaData( jsonSerializeMetadataIncludeSub )
  334.                     Local nojsonfMeta:String = parentField.MetaData( jsonSerializeMetadataExcludeSub )
  335.                     If jsonfMeta
  336.                         jsonf = jsonfMeta.split(",")
  337.                     ElseIf nojsonfMeta
  338.                         nojsonf = nojsonfMeta.split(",")
  339.                     EndIf
  340.                 EndIf
  341.                 ' look for {json} tags
  342.                 Local serializeMetadataOnly:Int = False
  343.                 For Local member:TField = EachIn typeid.EnumFields()
  344.                     If member.MetaData( jsonSerializeMetadataInclude )
  345.                         serializeMetadataOnly = True
  346.                         Exit
  347.                     EndIf
  348.                 Next
  349.                 ' build the dict
  350.                 Local dict:jsonValueDict = jsonValueDict.Create()
  351.                 For Local member:TField = EachIn typeid.EnumFields()
  352.                     Local add:Int = False
  353.                     If nojsonf And metaContains( nojsonf, member.Name() )
  354.                         add = False
  355.                     ElseIf jsonf
  356.                         add = metaContains( jsonf, member.Name() )
  357.                     ElseIf  member.MetaData( jsonSerializeMetadataIncludeSub ) Or ..
  358.                             member.MetaData( jsonSerializeMetadataExcludeSub ) Or ..
  359.                             member.MetaData( jsonSerializeMetadataBoolean ) Or ..
  360.                             member.MetaData( jsonSerializeMetadataClassSub )
  361.                         add = True
  362.                     Else
  363.                         ' {json}
  364.                         Local incl:Int = ( (Not serializeMetadataOnly) Or member.MetaData( jsonSerializeMetadataInclude ) )
  365.                         ' {nojson}
  366.                         Local excl:Int = member.MetaData( jsonSerializeMetadataExclude ) <> Null
  367.                         ' result
  368.                         add = incl And Not excl
  369.                     EndIf
  370.                     If add dict.add( member.Name(), serializeField( obj, member ) )
  371.                 Next
  372.                 Return dict
  373.             EndIf
  374.         EndIf
  375.     End Method
  376.     Method serializeField:jsonValue( obj:Object, member:TField )
  377.         Local id:TTypeId = member.TypeId()
  378.         Local p:Byte Ptr = bbRefFieldPtr( obj, member._index )
  379.         If id = ByteTypeId
  380.             Return jsonValueInt.Create( (Byte Ptr p)[0] )
  381.         ElseIf id = ShortTypeId
  382.             Return jsonValueInt.Create( (Short Ptr p)[0] )
  383.         ElseIf id = IntTypeId
  384.             If member.MetaData( jsonSerializeMetadataBoolean )
  385.                 Return jsonValueBool.Create( (Int Ptr p)[0] )
  386.             Else
  387.                 Return jsonValueInt.Create( (Int Ptr p)[0] )
  388.             EndIf
  389.         ElseIf id = LongTypeId
  390.             Return jsonValueInt.Create( (Long Ptr p)[0] )
  391.         ElseIf id = FloatTypeId
  392.             Return jsonValueFloat.Create( (Float Ptr p)[0] )
  393.         ElseIf id = DoubleTypeId
  394.             Return jsonValueFloat.Create( (Double Ptr p)[0] )
  395.         Else
  396.             Return serializeObject( bbRefGetObject( p ), member )
  397.         EndIf
  398.     End Method
  399.     Function metaContains:Int( array:String[], sub:String )
  400.         For Local str:String = EachIn array
  401.             If matchWild( str, sub ) Return True
  402.         Next
  403.         Return False
  404.     End Function
  405.    
  406.     ' Make jsonValueList from 1D array
  407.     Function jsonSerialize1DArray:jsonValueList( obj:Object, controller:jsonSerializationController )
  408.         Local typeid:TTypeId = TTypeId.ForObject( obj )
  409.         Local elementid:TTypeId = typeid.ElementType()
  410.         Local list:jsonValueList = jsonValueList.Create()
  411.         If elementid = ByteTypeId
  412.             Local array:Byte[] = Byte[] obj
  413.             For Local member:Int = 0 Until array.length
  414.                 list.add( jsonValueInt.Create( array[ member ] ) )
  415.             Next
  416.         ElseIf elementid = ShortTypeId
  417.             Local array:Short[] = Short[] obj
  418.             For Local member:Int = 0 Until array.length
  419.                 list.add( jsonValueInt.Create( array[ member ] ) )
  420.             Next
  421.         ElseIf elementid = IntTypeId
  422.             Local array:Int[] = Int[] obj
  423.             For Local member:Int = 0 Until array.length
  424.                 list.add( jsonValueInt.Create( array[ member ] ) )
  425.             Next
  426.         ElseIf elementid = LongTypeId
  427.             Local array:Long[] = Long[] obj
  428.             For Local member:Int = 0 Until array.length
  429.                 list.add( jsonValueInt.Create( array[ member ] ) )
  430.             Next
  431.         ElseIf elementid = FloatTypeId
  432.             Local array:Float[] = Float[] obj
  433.             For Local member:Int = 0 Until array.length
  434.                 list.add( jsonValueFloat.Create( array[ member ] ) )
  435.             Next
  436.         ElseIf elementid = DoubleTypeId
  437.             Local array:Double[] = Double[] obj
  438.             For Local member:Int = 0 Until array.length
  439.                 list.add( jsonValueFloat.Create( array[ member ] ) )
  440.             Next
  441.         ElseIf elementid = StringTypeId
  442.             Local array:String[] = String[] obj
  443.             For Local member:Int = 0 Until array.length
  444.                 list.add( jsonValueString.Create( array[ member ] ) )
  445.             Next
  446.         Else
  447.             Local array:Object[] = Object[] obj
  448.             For Local member:Int = 0 Until array.length
  449.                 list.add( jsonSerializer( controller ).serializeObject( array[ member ] ) )
  450.             Next
  451.         EndIf
  452.         Return list
  453.     End Function
  454.    
  455. End Type
  456.  
  457. ' Recusively turn jsonValue objects, which can be decoded from a string, into an arbitrary class with the specified fields
  458. Type jsonDeserializer Extends jsonSerializationController
  459.    
  460.     ' Initialize with standard deserializers (String, TList, and TMap)
  461.     ' Easy to add your own handlers upon calling init. Example call:
  462.     ' json.init( [ MyFirstTypeId, MySecondTypeId ], [ jsonDeserialize1Type, jsonDeserialize2Type ] )
  463.     Method init:jsonDeserializer( ..
  464.         moreHandlersTypeId:TTypeId[] = Null, ..
  465.         moreHandlersFunc:Object( obj:Object, member:TMember, controller:jsonSerializationController )[] = Null ..
  466.     )
  467.         addTypeHandler( StringTypeId, jsonDeserializeString )
  468.         addTypeHandler( ListTypeId, jsonDeserializeList )
  469.         addTypeHandler( MapTypeId, jsonDeserializeMap )
  470.         If moreHandlersTypeId And moreHandlersFunc
  471.             For Local i:Int = 0 Until moreHandlersTypeId.length
  472.                 addTypeHandler( moreHandlersTypeId[i], moreHandlersFunc[i] )
  473.             Next
  474.         EndIf
  475.         Return Self
  476.     End Method
  477.    
  478.     ' Deserialize an object
  479.     Method deserializeObject:Object( json:jsonValue, typeid:TTypeId, parentField:TField = Null )
  480.         If jsonValueNull( json )
  481.             Return Null
  482.         ElseIf typeHandlers.Contains( typeid )
  483.             Local handler:jsonTypeHandlerFunc = jsonTypeHandlerFunc( typeHandlers.ValueForKey( typeid ) )
  484.             Return handler.handle( json, parentField, Self )
  485.         Else
  486.             Local obj:Object = typeid.NewObject()
  487.             Local dict:jsonValueDict = jsonValueDict( json )
  488.             For Local member:TField = EachIn typeid.EnumFields()
  489.                 Local value:jsonValue = jsonValue( dict.get().ValueForKey( member.Name() ) )
  490.                 If value
  491.                     Local p:Byte Ptr = bbRefFieldPtr( obj, member._index )
  492.                     Local id:TTypeId = member.TypeId()
  493.                     deserializeMember( value, p, id, member )
  494.                 EndIf
  495.             Next
  496.             Return obj
  497.         EndIf
  498.     End Method
  499.     Method deserializeMember( value:jsonValue, p:Byte Ptr, typeid:TTypeId, parentField:TField )
  500.         If typeid.ElementType()
  501.             Local list:jsonValueList = jsonValueList( value )
  502.             If list
  503.                 Local arrayLength:Int = list.get().count()
  504.                 Local arrayType:TTypeId = typeid.ElementType()
  505.                 Local array:Object = typeid.NewArray( arrayLength )
  506.                 Local arrayIndex:Int = 0
  507.                 For Local element:jsonValue = EachIn list.get()
  508.                     Local p:Byte Ptr = bbRefArrayElementPtr( typeid.ElementType()._size, array, arrayIndex )
  509.                    
  510.                     deserializeMember( element, p, arrayType, parentField )
  511.                     arrayIndex :+ 1
  512.                 Next
  513.             Else
  514.                 Local array:Object = typeid.NewArray( 0 )
  515.                 assignObject( p, array )
  516.             EndIf
  517.  
  518.         ElseIf typeid = ByteTypeId
  519.             Local number:Byte = 0
  520.             If jsonValueBool( value )
  521.                 number = jsonValueBool( value ).get()
  522.             ElseIf jsonValueInt( value )
  523.                 number = jsonValueInt( value ).get()
  524.             ElseIf jsonValueFloat( value )
  525.                 number = jsonValueFloat( value ).get()
  526.             ElseIf jsonValueString( value )
  527.                 number = Byte jsonValueString( value ).get()
  528.             EndIf
  529.             (Byte Ptr p)[0] = number
  530.        
  531.         ElseIf typeid = ShortTypeId
  532.             Local number:Short = 0
  533.             If jsonValueBool( value )
  534.                 number = jsonValueBool( value ).get()
  535.             ElseIf jsonValueInt( value )
  536.                 number = jsonValueInt( value ).get()
  537.             ElseIf jsonValueFloat( value )
  538.                 number = jsonValueFloat( value ).get()
  539.             ElseIf jsonValueString( value )
  540.                 number = Short jsonValueString( value ).get()
  541.             EndIf
  542.             (Short Ptr p)[0] = number
  543.        
  544.         ElseIf typeid = IntTypeId
  545.             Local number:Int = 0
  546.             If jsonValueBool( value )
  547.                 number = jsonValueBool( value ).get()
  548.             ElseIf jsonValueInt( value )
  549.                 number = jsonValueInt( value ).get()
  550.             ElseIf jsonValueFloat( value )
  551.                 number = jsonValueFloat( value ).get()
  552.             ElseIf jsonValueString( value )
  553.                 number = Int jsonValueString( value ).get()
  554.             EndIf
  555.             (Int Ptr p)[0] = number
  556.            
  557.         ElseIf typeid = LongTypeId
  558.             Local number:Long = 0
  559.             If jsonValueBool( value )
  560.                 number = jsonValueBool( value ).get()
  561.             ElseIf jsonValueInt( value )
  562.                 number = jsonValueInt( value ).get()
  563.             ElseIf jsonValueFloat( value )
  564.                 number = jsonValueFloat( value ).get()
  565.             ElseIf jsonValueString( value )
  566.                 number = Long jsonValueString( value ).get()
  567.             EndIf
  568.             (Long Ptr p)[0] = number
  569.            
  570.         ElseIf typeid = FloatTypeId
  571.             Local number:Float = 0
  572.             If jsonValueBool( value )
  573.                 number = jsonValueBool( value ).get()
  574.             ElseIf jsonValueInt( value )
  575.                 number = jsonValueInt( value ).get()
  576.             ElseIf jsonValueFloat( value )
  577.                 number = jsonValueFloat( value ).get()
  578.             ElseIf jsonValueString( value )
  579.                 number = Float jsonValueString( value ).get()
  580.             EndIf
  581.             (Float Ptr p)[0] = number
  582.            
  583.         ElseIf typeid = DoubleTypeId
  584.             Local number:Double = 0
  585.             If jsonValueBool( value )
  586.                 number = jsonValueBool( value ).get()
  587.             ElseIf jsonValueInt( value )
  588.                 number = jsonValueInt( value ).get()
  589.             ElseIf jsonValueFloat( value )
  590.                 number = jsonValueFloat( value ).get()
  591.             ElseIf jsonValueString( value )
  592.                 number = Double jsonValueString( value ).get()
  593.             EndIf
  594.             (Double Ptr p)[0] = number
  595.            
  596.         Else
  597.             assignObject( p, deserializeObject( value, typeid, parentField ) )
  598.            
  599.         EndIf
  600.     End Method
  601.     Function assignObject( p:Byte Ptr, value:Object )
  602.         If value
  603.             Local id:TTypeId = TTypeId.ForObject( value )
  604.             Local class:Int = bbRefGetObjectClass( value )
  605.             While class And class <> id._class
  606.                 class = bbRefGetSuperClass( class )
  607.             Wend
  608.         EndIf
  609.         bbRefAssignObject( p, value )
  610.     End Function
  611.    
  612.     ' Determine TypeId that jsonValue should deserialize to when bmax doesn't explitly specify
  613.     ' (e.g. objects within a list)
  614.     Function jsonValueTypeId:TTypeId( value:jsonValue, member:TMember )
  615.         Local jsonc:String
  616.         If member jsonc = member.MetaData( jsonSerializeMetadataClassSub )
  617.         If jsonc
  618.             Return TTypeId.ForName( jsonc )
  619.         ElseIf jsonValueString( value )
  620.             Return StringTypeId
  621.         ElseIf jsonValueList( value )
  622.             Return ListTypeId
  623.         ElseIf jsonValueDict( value )
  624.             Return MapTypeId
  625.         Else
  626.             Return ObjectTypeId
  627.         EndIf
  628.     End Function
  629.      
  630. End Type
  631.  
  632. ' Base json value type
  633. Type jsonValue
  634.  
  635.     ' Get json string from object
  636.     Method toString:String()
  637.         Return Null
  638.     End Method
  639.    
  640.     ' Get object from json string
  641.     Function fromString:jsonValue( str:String, low:Int = 0, high:Int = 0 )
  642.         If high = 0 high = str.length
  643.        
  644.         ' Get next occurence of character in same scope, considering quotes, brackets, etc.
  645.         Function nextChar:Int( str:String, char:Int, low:Int, high:Int )
  646.             Local nestBrace:Int = 0, nestBracket:Int = 0, inQuote:Int = False
  647.             Local i:Int = low
  648.             While i < high
  649.                 If nestBrace = 0 And nestBracket = 0 And inQuote = False And str[i] = char
  650.                     Return i
  651.                 ElseIf str[i] = jsonEscapeAsc
  652.                     i :+ 1
  653.                 ElseIf str[i] = jsonStringAsc
  654.                     inQuote = Not inQuote
  655.                 ElseIf str[i] = jsonOpenBraceAsc
  656.                     nestBrace :+ 1
  657.                 ElseIf str[i] = jsonOpenBracketAsc
  658.                     nestBracket :+ 1
  659.                 ElseIf str[i] = jsonCloseBraceAsc
  660.                     nestBrace :- 1
  661.                 ElseIf str[i] = jsonCloseBracketAsc
  662.                     nestBracket :- 1
  663.                 EndIf
  664.                 i :+ 1
  665.             Wend
  666.             Return -1
  667.         End Function
  668.        
  669.         ' Skip whitespace
  670.         While str[ low ] < 32
  671.             low :+ 1
  672.         Wend
  673.        
  674.         ' Parse string
  675.         If str[ low ] = jsonStringAsc
  676.             Return jsonValueString.Create( jsonParseString( str, low, high ) )
  677.            
  678.         ' Parse dict
  679.         ElseIf str[ low ] = jsonOpenBraceAsc
  680.             Local dict:jsonValueDict = jsonValueDict.Create()
  681.             Local i:Int = low+1
  682.             While i < high-1
  683.                 Local assign:Int = nextChar( str, jsonAssignmentAsc, i, high-1 )
  684.                 Local delim:Int = nextChar( str, jsonDelimiterAsc, i, high-1 )
  685.                 Assert assign >= 0, "Syntax error: Missing assignment character"
  686.                 Local key:String = jsonParseString( str, i, assign )
  687.                 If delim >= 0
  688.                     Local value:jsonValue = jsonValue.fromString( str, assign+1, delim )
  689.                     dict.add( key, value )
  690.                     i = delim+1
  691.                 Else
  692.                     Local value:jsonValue = jsonValue.fromString( str, assign+1, high-1 )
  693.                     dict.add( key, value )
  694.                     Exit
  695.                 EndIf
  696.             Wend
  697.             Return dict
  698.        
  699.         ' Parse list
  700.         ElseIf str[ low ] = jsonOpenBracketAsc
  701.             Local list:jsonValueList = jsonValueList.Create()
  702.             Local i:Int = low+1
  703.             While i < high-1
  704.                 Local delim:Int = nextChar( str, jsonDelimiterAsc, i, high-1 )
  705.                 If delim >= 0
  706.                     list.add( jsonValue.fromString( str, i, delim ) )
  707.                     i = delim+1
  708.                 Else
  709.                     list.add( jsonValue.fromString( str, i, high-1 ) )
  710.                     Exit
  711.                 EndIf
  712.             Wend
  713.             Return list
  714.        
  715.         ' Parse others
  716.         Else
  717.             Local sub:String = str[ low..high ]
  718.            
  719.             ' Parse number
  720.             If str[ low ] >= 48 And str[ low ] <= 57
  721.                 Local isDecimal:Int = False
  722.                 For Local i:Int = low Until high
  723.                     If str[i] = jsonDecimalAsc isDecimal = True; Exit
  724.                 Next
  725.                 If isDecimal
  726.                     Return jsonValueFloat.Create( Double( sub ) )
  727.                 Else
  728.                     Return jsonValueInt.Create( Long( sub ) )
  729.                 EndIf
  730.            
  731.             ' Parse keywords
  732.             ElseIf sub = "null"
  733.                 Return jsonValueNull.Create()
  734.             ElseIf sub = "true"
  735.                 Return jsonValueBool.Create( True )
  736.             ElseIf sub = "false"
  737.                 Return jsonValueBool.Create( False )
  738.                
  739.             EndIf
  740.         EndIf
  741.        
  742.         Throw "Syntax error: Unexpected character "+Chr( str[ low ] )
  743.     End Function
  744.    
  745.     ' Utility functions for strings
  746.     Function jsonSanitizeString:String( str:String )
  747.         Return str.Replace( "~q", "~q" )
  748.     End Function
  749.     Function jsonDesanitizeString:String( str:String )
  750.         Return str.Replace( "~q", "~q" )
  751.     End Function
  752.     Function jsonParseString:String( str:String, low:Int, high:Int )
  753.         Local sub:String = str[ low..high ]
  754.         sub = sub.Trim()
  755.         Local qstart:Int = (sub[0] = jsonStringAsc)
  756.         Local qend:Int = (sub[ sub.length-1 ] = jsonStringAsc) And (sub.length < 2 Or sub[ sub.length-2 ] <> jsonEscapeAsc)
  757.         If qstart Or qend sub = sub[ qstart .. sub.length-qend ]
  758.         Return jsonDesanitizeString( sub )
  759.     End Function
  760.    
  761. End Type
  762.  
  763. ' Null type
  764. Type jsonValueNull Extends jsonValue
  765.     Function Create:jsonValueNull()
  766.         Return New jsonValueNull
  767.     End Function
  768.     Method toString:String()
  769.         Return "null"
  770.     End Method
  771.     Method get:Object()
  772.         Return Null
  773.     End Method
  774. End Type
  775.  
  776. ' Boolean type
  777. Type jsonValueBool Extends jsonValue
  778.     Field value:Int
  779.     Function Create:jsonValueBool( value:Int = 0:Int )
  780.         Local this:jsonValueBool = New jsonValueBool; this.value = value; Return this
  781.     End Function
  782.     Method toString:String()
  783.         If value Return "true" Else Return "false"
  784.     End Method
  785.     Method get:Int()
  786.         Return value
  787.     End Method
  788.     Method set( value:Int )
  789.         Self.value = value
  790.     End Method
  791. End Type
  792.  
  793. ' Integer type
  794. Type jsonValueInt Extends jsonValue
  795.     Field value:Long
  796.     Function Create:jsonValueInt( value:Long = 0:Long )
  797.         Local this:jsonValueInt = New jsonValueInt; this.value = value; Return this
  798.     End Function
  799.     Method toString:String()
  800.         Return String( value )
  801.     End Method
  802.     Method get:Long()
  803.         Return value
  804.     End Method
  805.     Method set( value:Long )
  806.         Self.value = value
  807.     End Method
  808. End Type
  809.  
  810. ' Float type
  811. Type jsonValueFloat Extends jsonValue
  812.     Field value:Double
  813.     Function Create:jsonValueFloat( value:Double = 0:Double )
  814.         Local this:jsonValueFloat = New jsonValueFloat; this.value = value; Return this
  815.     End Function
  816.     Method toString:String()
  817.         Return String( value )
  818.     End Method
  819.     Method get:Double()
  820.         Return value
  821.     End Method
  822.     Method set( value:Double )
  823.         Self.value = value
  824.     End Method
  825. End Type
  826.  
  827. ' String type
  828. Type jsonValueString Extends jsonValue
  829.     Field value:String
  830.     Function Create:jsonValueString( value:String = "" )
  831.         Local this:jsonValueString = New jsonValueString; this.value = value; Return this
  832.     End Function
  833.     Method toString:String()
  834.         Return "~q" + jsonSanitizeString( value ) + "~q"
  835.     End Method
  836.     Method get:String()
  837.         Return value
  838.     End Method
  839.     Method set( value:String )
  840.         Self.value = value
  841.     End Method
  842. End Type
  843.  
  844. ' List type
  845. Type jsonValueList Extends jsonValue
  846.     Field value:TList
  847.     Function Create:jsonValueList( value:TList = Null )
  848.         If value = Null value = CreateList()
  849.         Local this:jsonValueList = New jsonValueList; this.value = value; Return this
  850.     End Function
  851.     Method toString:String()
  852.         Local content:String = "", first:Int = True
  853.         For Local member:jsonValue = EachIn value
  854.             If first first = False Else content :+ ","
  855.             content :+ member.toString()
  856.         Next
  857.         Return "[" + content + "]"
  858.     End Method
  859.     Method get:TList()
  860.         Return value
  861.     End Method
  862.     Method set( value:TList )
  863.         Self.value = value
  864.     End Method
  865.    
  866.     Method add:TLink( value:jsonValue )
  867.         Return Self.value.addlast( value )
  868.     End Method
  869. End Type
  870.  
  871. ' Dict type
  872. Type jsonValueDict Extends jsonValue
  873.     Field value:TMap
  874.     Function Create:jsonValueDict( value:TMap = Null )
  875.         If value = Null value = CreateMap()
  876.         Local this:jsonValueDict = New jsonValueDict; this.value = value; Return this
  877.     End Function
  878.     Method toString:String()
  879.         Local content:String = "", first:Int = True
  880.         For Local key:Object = EachIn value.Keys()
  881.             If first first = False Else content :+ ","
  882.             Local member:Object = value.ValueForKey( key )
  883.             content :+ "~q" + jsonSanitizeString( String( key ) ) + "~q:" + jsonValue( member ).toString()
  884.         Next
  885.         Return "{" + content + "}"
  886.     End Method
  887.    
  888.     Method get:TMap()
  889.         Return value
  890.     End Method
  891.     Method set( value:TMap )
  892.         Self.value = value
  893.     End Method
  894.     Method add( key:Object, value:jsonValue )
  895.         Self.value.insert( key, value )
  896.     End Method
  897. End Type


Comments : none...

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal