Roku Developer Program

Join our online forum to talk to Roku developers and fellow channel creators. Ask questions, share tips with the community, and find helpful resources.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
TheEndless
Channel Surfer

Re: Built-in ToJSON function

I think I found out why the FormatJSON function isn't published... Any objects with circular references will blow up the Roku.

For example:
    parentObject = {}
childObject = {}
parentObject.Child = childObject
childObject.Parent = parentObject
?FormatJSON(parentObject) ' <== Roku go BOOM
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
EnTerr
Roku Guru

Re: Built-in ToJSON function

"TheEndless" wrote:
I think I found out why the FormatJSON function isn't published... Any objects with circular references will blow up the Roku.

Fun discovery (YAWRR)!
One would consider this a bug only if they think BRS shouldn't have a reboot command 8-)

Musings: JSON cannot per-se represent structures with data cycles. It covers only a subset of "directed acyclic graphs". Non-corrupt trees, to be more specific (tree data structure where the root has no parent and each other node has exactly 1 parent). If not obvious how those are related, imagine drawing dictionary's root as a node with edges (labeled with the keys) pointing to value nodes. Array node A will have edges (labeled 0, 1, ...) pointing to the values as nodes A[0], A[1]... and that's it.

The example gives is a corrupt tree - "corrupted" by a cycle. And that can be done even with a single node:
InfiniteLoop = {}  'Cupertino, CA 95014
InfiniteLoop.next = InfiniteLoop
formatJSON(InfiniteLoop)

But there can be acyclic corrupted tree too, for example this
BrightScript Debugger> A = [1]      
BrightScript Debugger> t = {left:A, right:A}
BrightScript Debugger> ?formatJSON(t)
{"left":[1],"right":[1]}
This does not cause crash but the serialization does not represent the fact that both "left" and "right" were pointing to the same array (or node A has two parents, left and right). There are libraries that can correctly handle serialization of general directed graphs (e.g. Python's "pickle") but that's not really in JSON's job description. So it's the wrong thing to send such data to JSON.

The easiest way to wash hands is to document that the function works correct only for "proper trees". More elaborate one would be to change the code so during serialization it keeps track of already visited nodes (in a set/AA) and if a node is re-visited to blow up with "corrupt tree" error; that will take care of both cases above.

PS. Bonus bug in formatJSON - it only seems to work for AAs, but not for lists. Not a proper JSON:
BrightScript Debugger> ?formatJSON([1,2,3])

0 Kudos
EnTerr
Roku Guru

Re: Built-in ToJSON function

As an update, at some point over the last 8 months, formatJSON has lost its undocumented status and has quietly been officiated a legal permanent resident. El papeles: BrightScript Language Reference # 7.19 FormatJson()

Seems some additional work was put into it. In particular `formatJSON([1, 2, 3])` works now. In addition, cyclic structures no more reboot Roku
BrightScript Debugger> h = {}: h.h = h
BrightScript Debugger> s = formatJSON(h)
BRIGHTSCRIPT: ERROR: FormatJSON: Maximum nesting depth exceeded
BrightScript Debugger> ? s

but the behavior with that or an "acyclic corrupted tree" remains undocumented.

Sadly, still does not work on firmware 3 models. All in all, i recommend using my toJSON() instead.
0 Kudos
RokuKevin
Visitor

Re: Built-in ToJSON function

Please note that we're only updating the firmware on Pico devices in extreme circumstances to roll out security updates or other patches for critical bugs that require a hotfix.

Enhancements to the API like adding FormatJSON() function to the API will not be added to the v3.1 branch.

I would encourage you to utilize the firmware implementation on newer platforms to take advantage of the optimizations we can do in native code that cannot be done in Brightscript and implement your own brightscript version to support v3.1. There are several ways to implement an abstraction layer around this but you could do something simple like:

function toJSON(json as Object, flags = 0 as Integer) as String
if firmwareFourOrGreater() then
return formatJSON(json, flags)
else
REM your implementation here
end if
end function

Function firmwareFourOrGreater() As Boolean
version = CreateObject("roDeviceInfo").GetVersion()

major = Mid(version, 3, 1)

if Val(major) < 4 then
return false
end if
return true
End Function


--Kevin
0 Kudos
EnTerr
Roku Guru

Re: Built-in ToJSON function

"RokuKevin" wrote:
Please note that we're only updating the firmware on Pico devices in extreme circumstances to roll out security updates or other patches for critical bugs that require a hotfix.

Enhancements to the API like adding FormatJSON() function to the API will not be added to the v3.1 branch.

"Pico"... it must be code-name for the NXP MIPS platform.

RokuKevin -
this sounds like an end-of-life for the 1xxx/2xxx models, can you post notice prominently (this forum and/or SDK wiki)? Something to state teh legacy boxes will always stay on firmware 3.x (so we don't have to ass-ume what to check for) and no new features/enhancements will be added but critical/security fixes.

And also, could you please, please (und noch ein Zucker Berg oben) mark clearly in the docs which functions are not supported in fw3? There are only 2 levels of the platform to speak of, so cannot say is too hard to maintain. formatJSON() is currently not marked with version#, implication being it works everywhere. Such un-labeling is not an isolated case, I have burned myself before.
0 Kudos
RokuKC
Roku Employee
Roku Employee

Re: Built-in ToJSON function

Thanks for the documentation feedback.

A note has been added to the FormatJSON documentation to indicate that it is not supported on the 3.1 firmware.
0 Kudos
dcrandall
Visitor

Re: Built-in ToJSON function

"kurtn718" wrote:
Hi. Is there a built-in ToJSON function, similar to ParseJSON?

I couldn't find one in the docs, but I was able to write the following function taking an roAssociativeArray as the input - which works for what I need.

If there is a built-in function, I'll probably replace the code below with that function. If one doesn't exist, then this is my first contribution to the forum. 🙂 If there is a way to write the code better, please offer suggestions.

Thanks,

Kurt
-------
Function ToJSON(jsonData as Object) as String
jsonRequest = "{"
for each key in jsonData
if jsonRequest <> "{" then
jsonRequest = jsonRequest + ","
endif

jsonRequest = jsonRequest + chr(34) + key + chr(34) + " : " + chr(34) + jsonData[key] + chr(34)
end for
jsonRequest = jsonRequest + "}"

return jsonRequest
End Function


Just as a note for posterity, you might have to use the roAssociativeArray function "AddReplace" to preserve case-sensitivity.
0 Kudos
EnTerr
Roku Guru

Re: Built-in ToJSON function

"dcrandall" wrote:
Just as a note for posterity, you might have to use the roAssociativeArray function "AddReplace" to preserve case-sensitivity.

You mean before externalizing to JSON, while creating the data structure - if one does not want all keys to turn lowercase?

Using AddReplace() is not enough though. `dict.AddReplace(key, value)` is equivalent to `dict[key] = value` - it will preserve the key case once but if another key2 is used later that differs in letter-case only, that will clobber the first one:
BrightScript Debugger> h = { }
BrightScript Debugger> h["KeY"] = "value"
BrightScript Debugger> ? h
KeY: value

BrightScript Debugger> h["kEy"] = "eulaV"
BrightScript Debugger> ? h
kEy: eulaV

The solution includes two steps:

  1. Call SetModeCaseSensitive() on the hashes destined for case-sensitive websites

  2. Don't use dot-operator to assign them, use []= or AddReplace() instead
0 Kudos