EnTerr
12 years agoRoku Guru
toJSON() - a working JSON generator
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
And so, without further adieu:
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.
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.