Code archives/Algorithms/Convert an Object to JSON
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| This does about what it says, using reflection. The only thing that doesn't really work too well is multidim arrays, because those are grade-A insanity to work with. Objects that have already been converted are referenced only by their handle (which is, to be perfectly honest, just the their memory address, but it works well enough) - no type name is provided for them, since they've already been mentioned at least once before the current object/field/what have you in the JSON. To use it, you call ObjectToJSON(obj, map) where obj is the object you want to convert to JSON and map is a TMap you'll use to store which objects have already been converted to JSON. If objects have dependencies on one-another or reference each other, you should try to create one TMap for that group of objects so as to reduce the size of the JSON output. | |||||
SuperStrict
Private
Function _objtoptr:Byte Ptr(obj:Byte Ptr)
Return obj
End Function
Global ObjToPtr:Byte Ptr(obj:Object)=Byte Ptr(_objtoptr)
Function FieldToJSON:String(fid:TField, forObject:Object, map:TMap)
Select fid.TypeId()
Case IntTypeId,ShortTypeId,ByteTypeId
Return String(fid.GetInt(forObject))
Case LongTypeId
Return String(fid.GetLong(forObject))
Case DoubleTypeId
Return String(fid.GetDouble(forObject))
Case FloatTypeId
Return String(fid.GetFloat(forObject))
Default
Return ObjectToJSON(fid.Get(forObject), map)
End Select
End Function
Public
' The map argument is used to record what objects have already been put in a prior part of any JSON string
Function ObjectToJSON:String(obj:Object, map:TMap)
Assert map Else "Inspection map not present"
Local name$ = Int(ObjToPtr(obj))
Local result$
Local tid:TTypeId = TTypeId.ForObject(obj)
If tid = Null Then
Return "null"
EndIf
If tid <> StringTypeId And tid._class <> ArrayTypeId._class And map.Contains(name) Then
Return "{~qhandle~q: "+name+"}"
EndIf
map.Insert(name,name)
If tid._class = ArrayTypeId._class Then
If tid.Name().StartsWith("Null[") Then
Return "null"
EndIf
result = "{~qhandle~q: "+name+", ~qtype~q: ~q"+tid.Name()+"~q"
Local elemt:TTypeId = tid.ElementType()
If elemt Then
Local dimensions:Int = tid.ArrayDimensions(obj)
result :+ ", dimensions: ["
Local dimString$ = ""
Local last:Int = 1
For Local dim:Int = dimensions-1 To 0 Step -1
Local dimLength:Int = tid.ArrayLength(obj, dim)
If dim < dimensions-1 Then
dimString = ", " + dimString
EndIf
dimString = (dimLength / last) + dimString
last = dimLength
Next
result :+ dimString
result :+ "]"
Local elems:String[] = New String[tid.ArrayLength(obj, 0)]
For Local i:Int = 0 Until elems.Length
Select elemt
Case IntTypeId, ShortTypeId, ByteTypeId, LongTypeId, DoubleTypeId, FloatTypeId
elems[i] = String(tid.GetArrayElement(obj, i))
Default
elems[i] = ObjectToJSON(tid.GetArrayElement(obj, i), map)
End Select
Next
result :+ ", ~qcontent~q: ["+(", ".Join(elems))+"]}"
Else
result :+ ", ~qcontent~q: []}"
EndIf
ElseIf tid = StringTypeId
If String(obj) = Null Then
Return "~q~q"
EndIf
result = "~q" + String(obj).Replace("~q", "\~q").Replace("~n", "\n").Replace("~t", "\t").Replace("~r", "\r").Replace("~0", "\0") + "~q"
Else
If Not obj Then
Return "null"
EndIf
result = "{~qhandle~q: "+name+", ~qtype~q: ~q"+tid.Name()+"~q"
Local fields:TList = tid.EnumFields()
Local enum:TListEnum = fields.ObjectEnumerator()
While enum.HasNext()
Local fid:TField = TField(enum.NextObject())
result :+ ", ~q."+fid.Name()+"~q: "+FieldToJSON(fid, obj, map)
Wend
result :+ "}"
EndIf
Return result
End Function |
Comments
| ||
| Updated to have slightly better multidimensional array support. An array of integers called 'dimensions' will precede the 'content' field for storing array data. This contains the length of each array dimension (for some reason brl.Reflection's TTypeID#ArrayLength reports the length of a dimension in terms of every element accessible from a dimension onward, so this only includes the size of each dimension on its own). |
Code Archives Forum