JSON Serialisation & Deserialisation

BlitzMax Forums/BlitzMax Programming/JSON Serialisation & Deserialisation

Banshee(Posted 2014) [#1]
Hi

I wrote this class which I thought others might find useful so I decided to post it.

The class is designed to be extended off of, and allows the child to serialise itself to JSON or to rebuild it's data content from JSON.

It will not serialise method calls as it is specifically designed for storing data and I've only tested it with String, Integers and Arrays of Integers which is all that I need - although I think it will probably do Arrays of Strings without modification - for anything else you'll need to add support yourself.

Superstrict

Type JSON
	
	Method getJSON$()
		
		Local json$
		
		Local id:TTypeId = TTypeId.ForObject( self )
		For Local fld:TField = EachIn id.EnumFields()
			
			Local fieldName$ = Chr( 34 ) + fld.Name() + Chr( 34 ) + ":"
			Local elementName$ = fld.TypeId().Name()
			
			If( Right( elementName , 2 ) = "[]" )
			
				json :+ fieldName + "["
			
				Local arrayObject: object= fld.get( Self )
				Local arrayId:TTypeId = TTypeId.ForObject( arrayObject )
				Local length% = fld.typeId().ArrayLength( fld.get( Self ) , 0 )
				
				For Local i% = 0 To length - 1
					Local arrayElement:Object = arrayId.getArrayElement( arrayObject , i )
					json :+ arrayElement.toString() + ","
				next
				
				json = Left( json , Len( json ) - 1 ) + "],"
			
			else
				Select elementName
				
					Case "String"
						json :+ fieldName + Chr(34) + fld.getString$( Self ) + Chr(34) + ","
					Case "Int"
						json :+ fieldName + fld.getInt( Self ) + ","
					Default
						DebugLog( fld.Name() +" is of unprocessed type " + fld.typeId().Name() )
				End Select
			endIf
			
		Next
		
		Return "{" + Left( json , Len( json ) - 1 ) + "}"

	End Method
	
	Method setJSON( json$ )
		
		json = Left( Right( json , Len( json ) - 1 ) , Len( json ) - 1 ) + ","
		Local comma% = Instr( json , "," )
		Local square% = Instr( json , "[" )
		If square < comma Then comma = Instr( json , "]" , square ) + 1
		
		Local id:TTypeId = TTypeId.ForObject( Self )
		
		While( comma > 0 )

			Local this$ = Left( json , comma )
			Local colon% = Instr( this , ":" )
			Local cmd$ = Mid( this , 2 , colon - 3 )
			Local param$ = Mid( this , colon + 1 , Len( this ) - ( colon + 1 ) )
			Local paramInt% = 0
			
			Local fld:TField = id.findField( cmd )
			
			If( Left( param, 1 ) = "[" And Right( param, 1 ) = "]" )
				'array
				
				Local arrayObject: Object= fld.get( Self )
				Local arrayId:TTypeId = TTypeId.ForObject( arrayObject )
				Local length% = fld.typeId().ArrayLength( fld.get( Self ) , 0 )
				
				Local paramArray$[1]
				
				Local aparam$ = Mid( param , 2 , Len( param ) - 2 )
				
				For Local i% = 0 To length - 1
					Local acomma% = Instr( aparam , "," )
					paramArray[0] = Left( aparam , acomma - 1 )
					aparam = Right( aparam , Len( aparam ) - acomma )
					
					arrayId.setArrayElement( arrayObject , i , paramArray[0] )
										
				Next

			Else
				If( Left( param , 1 ) = Chr(34) And Right( param , 1 ) = Chr( 34 ) )
					'string
					param = Mid( param , 2 , Len( param ) - 2 )
					If fld Then fld.setString( Self , param )
				Else
					paramInt = Int( param )
					If fld Then fld.setInt( Self , paramInt )
				endIf
			EndIf
			If Not fld And Len( cmd ) > 1 Then DebugLog( "Could not find field " + cmd )
			
			json = Right( json , Len( json ) - comma )
			comma = Instr( json , "," ) 
			square = Instr( json , "[" )
			If square < comma Then comma = Instr( json , "]" , square ) + 1

		wend
		
	End method
	
End Type


And here's my test program showing sample usage.
Type TPlayer Extends JSON
	
	Field prefix$
	Field name$
	Field surname$
	Field suffix$
	
	Field race $, class$
	Field gender% , level% , face% , hair%
	Field strength% , strengthPercentile% , dexterity% , constitution% , inteligence% , wisdom% , charisma%
	Field thaco% , ac% , hp% , weaponDice%

	Field equipment%[20]
	

End Type


Local player:TPlayer = New TPlayer
player.prefix = "Miss"
player.name = "Becky"
player.surname = "Rose"
player.suffix = "Programmer"
player.race = "Human"
player.class = "Class"
player.gender = 0
player.level = 20
player.face = 0
player.hair = 0
player.strength = 8
player.dexterity = 12
player.constitution = 12
player.inteligence = 17
player.wisdom = 15
player.charisma = 9
player.thaco = 10
player.ac = 10
player.hp = 69
player.weaponDice = 8
player.equipment[0] = 1
player.equipment[1] = 2
player.equipment[2] = 3
player.equipment[3] = 4

Local test$ = player.getJSON()
DebugLog( test )

player = New TPlayer
player.setJSON( test )
DebugLog( player.getJSON() )



Who was John Galt?(Posted 2014) [#2]
Thanks for sharing, Banshee. This could save me quite a bit of code.


Banshee(Posted 2014) [#3]
You're very welcome. I'm also finding it REALLY handy in Debuglog :D


Richard Betson(Posted 2014) [#4]
Very timely for me :) Thanks.