Question On Use of Built-In JSON Parser and Generator

Good afternoon,

I’m closing in on getting this project working, but had a question on the new documentation for the JSON Parser and Generator in Device OS, recently posted by @rickkas7.

I have the following JSON, and the parser is only returning the top level JSON element in the object (if that’s the right terminology):

{ “daily”: [ { “dt”:1589475600, “rain”: } { “dt”:1589562000, “rain”: } { “dt”:1589648400, “rain”: } { “dt”:1589734800, “rain”: } { “dt”:1589821200, “rain”:8.32 } { “dt”:1589907600, “rain”:0.39 } { “dt”:1589994000, “rain”:1.28 } { “dt”:1590080400, “rain”:0.73 } ] }

The iterator returns “daily” and stops on the first iteration, based on this example code from the documentation.

I think what I need to know is how to iterate on an object within the key of another object, as stated here:

“Construct an iterator from a JSONValue. This can be the whole object, or you can use it to iterate an object within the key of another object.

Unfortunately, while the documentation has helped immensely, there’s no example of how to accomplish this specifically, and I’ve been unsuccessful through trial and error.

Simply put, how do I reference and or iterate through sub-elements of an object using this parser?

Thanks in advance for your time.

-RC

First, there are three problems with that JSON that make it not parsable:

  • The quotes are typographical quotes “daily” not straight quotes “daily” though that could have happened on copy/paste
  • Having a key without a value is not allowed "rain":}
  • There must be a comma separator between array elements

The corrected JSON looks like this:

 { "daily": [ { "dt":1589475600, "rain":0.0 },{ "dt":1589562000, "rain":0.0 },{ "dt":1589648400, "rain":0.0 },{ "dt":1589734800, "rain":0.0 },{ "dt":1589821200, "rain":8.32 },{ "dt":1589907600, "rain":0.39},{ "dt":1589994000, "rain":1.28 },{ "dt":1590080400, "rain":0.73 } ] }

The test code I used for iteration looks like this:

        JSONValue outerObj = JSONValue::parseCopy("{ \"daily\": [ { \"dt\":1589475600, \"rain\":0.0 },{ \"dt\":1589562000, \"rain\":0.0 },{ \"dt\":1589648400, \"rain\":0.0 },{ \"dt\":1589734800, \"rain\":0.0 },{ \"dt\":1589821200, \"rain\":8.32 },{ \"dt\":1589907600, \"rain\":0.39},{ \"dt\":1589994000, \"rain\":1.28 },{ \"dt\":1590080400, \"rain\":0.73 } ] }");

        JSONObjectIterator outerIter(outerObj);
        while(outerIter.next()) {
            if (outerIter.name() == "daily") {
                JSONArrayIterator arrayIter(outerIter.value());
                for(size_t ii = 0; arrayIter.next(); ii++) {
                    if (arrayIter.value().isObject()) {
                        JSONObjectIterator objectIter(arrayIter.value());
                        while(objectIter.next()) {
                            if (objectIter.name() == "dt") {
                                Log.info("ii=%u dt=%d", ii, objectIter.value().toInt());
                            }
                            else
                            if (objectIter.name() == "rain") {
                                Log.info("ii=%u rain=%lf", ii, objectIter.value().toDouble());
                            }
                        }
                    }
                }
            }
        }

And the output:

        0000007774 [app] INFO: ii=0 dt=1589475600
        0000007774 [app] INFO: ii=0 rain=0.000000
        0000007774 [app] INFO: ii=1 dt=1589562000
        0000007774 [app] INFO: ii=1 rain=0.000000
        0000007775 [app] INFO: ii=2 dt=1589648400
        0000007775 [app] INFO: ii=2 rain=0.000000
        0000007775 [app] INFO: ii=3 dt=1589734800
        0000007776 [app] INFO: ii=3 rain=0.000000
        0000007776 [app] INFO: ii=4 dt=1589821200
        0000007776 [app] INFO: ii=4 rain=8.320000
        0000007777 [app] INFO: ii=5 dt=1589907600
        0000007777 [app] INFO: ii=5 rain=0.390000
        0000007777 [app] INFO: ii=6 dt=1589994000
        0000007777 [app] INFO: ii=6 rain=1.280000
        0000007778 [app] INFO: ii=7 dt=1590080400
        0000007778 [app] INFO: ii=7 rain=0.730000
4 Likes

@rickkas7, thank you very much - I did run the JSON through a JSON validator that highlighted the lack of commas as invalid (didn’t catch the quotes, but I’ll double check to see if it’s a copy/paste thing). I’m not particularly familiar with JSON (but learning!), so I didn’t know if those format issues would be a deal-breaker or not.

I believe my response template is introducing the “rain” keys with no value since openweathermap.org “One Call API” only publishes the key and value if there is rain forecasted for that day of the week.

In terms of iterating through sub-elements, I thought maybe it would incorporate JSON arrays in addition to objects, just couldn’t figure out the implementation. Thank you for taking the time, you are a gentleman and a scholar.

-RC

The trick to dealing with the rain key is add a zero after the mustache template expansion for the rain key. The extra trailing zero won’t bother the floating point number, but when the key is missing there will be a zero, so it will be valid JSON.

For the comma separator, add the comma separator, however you’ll find that it still generates invalid JSON because of the trailing comma. To get around that, after the looping part, add a 0. Normally there would be an object there, but the code I pasted above checks to see if the object is an object so it will work.

Also, if you haven’t found it yet, you can greatly speed up development of mustache response templates by using my mustache tester

2 Likes

Helpful workarounds, but since this invalid JSON is created by a webhook template, it would be nice to see some tweaks on the templating logic for in the webhook system to allow for creating an always valid output.

The template in question came from this

Not adding the comma and adding the comma in the template will both times result in an invalid output. When a list is rendered (indicated by [ ... ]) shouldn't the template expansion know how to render the contents correctly?

How would one create a template that creates a valid JSON from a valid JSON other than that?

Also, shouldn't the template expansion render a 0 or "" result for non-present keys?
The added 0 only works when the value is a floatint point value, it would change the value for an integer and render invalid results for string values. Since the raw webhook response may well be rendering different types when the key is present, a generic approach would be needed.
An alternative would be to make the key creation in the expanded result depending on the presens of the value - but I don't see how to code that in the template either.

Shouldn't one of these work?

{ "daily": [{{#daily}} { "dt":{{dt}}, "rain":{{#if rain}} {{rain}} {{else}} 0.0 {{/if}} } {{/daily}}] }
  or
{ "daily": [{{#daily}} { "dt":{{dt}}, "rain":{{#with rain}} {{rain}} {{else}} 0.0 {{/with}} } {{/daily}}] }
 or
{ "daily": [{{#daily}} { "dt":{{dt}}, "rain":{{rain}} {{#unless rain}} 0.0 {{/unless}} } {{/daily}}] }

but neither render a result - apparently the if, with and unless helpers are not declared although they should be built-in

1 Like

@rickkas7 , I implemented your code and workarounds for the template - it’s working great. Also, thanks for pointing out your mustache tester, very useful.

1 Like