I figured i will dip my toes where my mouth is (yay, a
portmantidiom!), so i wrote a JSON serializer/stringifier function.
toJSON() takes nested BS data structures and returns passable JSON string. Unlike the undocumented built-in "formatJSON()", this one is
- Published (for trivial reasons: source provided)
- Documented (ditto; when in doubt, RTFS)
- Works on v3 platform as well as on v5
- Supports broader range of data (e.g. [1,2,3])
- Does not crash-reboot the player if fed a cyclic structure
- Malleable (oh, you would rather have tabs escaped as "\t"? so change it)
And so, without further
adieu:
function toJSON(it, TTL=40):
if TTL > 0:
typ = type(it)
if typ = "String" or typ = "roString":
'escape " and \. NB: cannot deal with \ *after* others are escaped
res = createObject("roRegex", "([\x22\\])", "").replaceAll(it, "\\\1")
'control chars; likely no need but rfc4627 calls for it
re = createObject("roRegEx", "[\x00-\x1f]", "")
bArr = invalid
while true:
ch = re.match(res)[0]
if ch = invalid then exit while
if ch = chr(10):
toRE = "\\n"
elseif ch = chr(13):
toRE = "\\r"
else:
if bArr = invalid then bArr = createObject("roByteArray")
bArr.fromAsciiString(ch)
toRE = "\\u00" + bArr.toHexString()
end if
res = createObject("roRegEx", ch, "").replaceAll(res, toRE)
end while
return chr(34) + res + chr(34)
elseif typ = "roArray" or typ = "roList":
res = ""
for each item in it:
res = res + toJSON(item, TTL-1) + ","
end for
'drop the last comma (if any)
return "[" + left(res, len(res)-1) + "]"
elseif typ = "roAssociativeArray":
res = ""
for each key in it:
res = res + toJSON(key, 1) + ":" + toJSON(it[key], TTL-1) + ","
end for
'drop the last comma (if any)
return "{" + left(res, len(res)-1) + "}"
elseif typ = "Integer" or typ = "roInt" or typ = "roInteger":
'yes, Virginia, there is roInteger: ?type([0][0])
return it.toStr()
elseif typ = "Float" or typ = "Double" or typ = "roFloat" or typ = "roDouble":
'str() is our first, last and only hope
return str(it).trim()
elseif typ = "Boolean" or typ = "roBoolean":
if it:
return "true"
else:
return "false"
end if
elseif typ = "Invalid" or typ = "roInvalid":
return "null"
else:
' "Function", "<uninitialized>", "if"-interfaces, other "ro"-objects
print "toJSON: unsupported type", type(it), it
STOP
end if
else: 'TTL<=0, time-to-live counter expired
print "toJSON: too many nested structures (likely a cycle)", it
STOP
end if
end function
It is all straightforward, except maybe the string escapes, where some attention was needed. I limited nesting at 40 since interpreter croaks at depth ~56 (no idea why so low but should suffice for JSON practical purposes). Comments welcome, esp. if you have some interesting or big structures to stringify.