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