Parsing nested json


#1

I didn’t see this asked anywhere else, but I figured it would help someone.

if you have nested json like this:

{
    "someNestedObject": {
         "someProperty": 25,
         "someOtherProperty": "a String"
    },
    "someOtherRegularProperty": false
}

you can access them like this:

juce::var parsedJson;
if( juce::JSON::parse(json, parsedJson).wasOK() ) {
    int value1 = parsedJson["someNestedObject"]["someProperty"];
    String value2 = parsedJson["someNestedObject"]["someOtherProperty"];
    bool value3 = parsedJson["someOtherRegularProperty"];
}

I hope someone else finds that useful!


Iterate var object
#2

Thanks. That is useful for me.


#3

Is there a way to CREATE JSON as easily as we can parse it from a correctly formatted JSON string?

something like:

JSONTree subTree; 
subTree.addNode( "a property", "a property value" );
subTree.addNode( "a 2nd property", "a 2nd property value" );
JSONTree tree;
JSONTree.addNode( "a nested tree", subTree );
JSONTree.addNode( "a non-nested property", "a non-nested value" );
JSONTree.writeToStream( someFile.createOutputStream() );

and that would give us a file on disk that looked like this:

{
    "a nested tree": {
        "a property": "a property value",
        "a 2nd property": "a 2nd property value"
    },
    "a non-nested property": "a non-nested value"
}

#4

Of course - JSON::parse and JSON::toString are symmetrical.


#5

But there’s no “JSONtree” class in Juce…Is there by the way a reason the JSON support in Juce does not work with ValueTrees?


#6

but that doesn’t explain how to create JSON trees.

I saw this post: Construct var programmatically and convert to JSON

and pneyrink created this helper method:

void varSetProperty(var& object, String key, var value){
    if (object.isObject() {
        object.getDynamicObject()->setProperty(key, value);
   }
}

with the following usage:

var myJSONObject(new DynamicObject());
varSetProperty(myJSONObject, "name", "Jules");

#7

It doesn’t need a special class! ‘var’ does the job of representing JSON/Javascript objects, so to create JSON, you just create a var. (Sorry, I thought that would be obvious by the way JSON::parse returns a var!)

ValueTree is different - its structure is analogous to XML, which can’t be mapped directly onto JSON.


#8

Perhaps we need some examples for how to create vars that have nested structures, similar to the text examples in this thread. Could ya take a minute and help out the users with some examples?


#9

If there was a non const version of const var& var::operator[] (const Identifier & propertyName) const you would be able to do:

var something (new DynamicObject());
something ["name"] = "Sally";

would that work?


#10

that would be rad. users would love that!


#11

Grepping for json::toString, here’s an example from the repo:

static String getiOSAssetContents (var images)
{
    DynamicObject::Ptr v (new DynamicObject());

    var info (new DynamicObject());
    info.getDynamicObject()->setProperty ("version", 1);
    info.getDynamicObject()->setProperty ("author", "xcode");

    v->setProperty ("images", images);
    v->setProperty ("info", info);

    return JSON::toString (var (v.get()));
}

#12

if you’re like me and you can’t figure out how to write json to disk using the JSON::writeToStream() static methods, you can do this:

File g = yourFile;
g.replaceWithText( juce::JSON::toString( yourJSONvar ) );

#13

Yep, nothing wrong with that!


#14

There have been so many times I wished we could do this!

something ["name"] = "Sally";

Both for var and ValueTree


#15

Why hasn’t it been added?

can’t you just overload the operator to create the property if it doesn’t exist?


#16
const var& var::operator[] (const Identifier& propertyName) const
{
    if (DynamicObject* const o = getDynamicObject())
        return o->getProperty (propertyName);

    return getNullVarRef();
}

this is the method in question.

Seems like we just need to change the getNullVarRef() line to something that creates a dynamic object, and populates it with propertyName

maybe something like this: ?

const var& var::operator[] (const Identifier& propertyName) const
{
    if (DynamicObject* const o = getDynamicObject())
        return o->getProperty (propertyName);

    auto newOb = DynamicObject();
    newOb.setProperty( propertyName, "" );
    append( newOb );
    return this[propertyName];
    
}

#17

Well, the var might have been something meaningfull, that you lose if you just turn it into a dynamic object…

var something = 5;
something ["name"] = "Sally";

In your case my 5 was gone…

The problem is not that the property didn’t exist, it is that the var thing stored in var cannot have properties.

I’d rather expect it to throw an exception or something similar…

But there should be a solution to that

EDIT: It keeps to bite me in the back, it needs to also add a property, when it didn’t exist already…


#18

I did a kludgy patch to make that work, here attached.
The problem is I cannot return a const var getNullValueReference(), because you cannot assign something to it.
As a workaround I create a static non const var, that swallows the assigned content.

Maybe @Jules has a better idea? Please… :wink:

assignablePropertiesPatch.txt (5.4 KB)


#19

Yes: give up!

The language just doesn’t lend itself to something like this. I don’t know if the standards committee would ever consider adding something like operator[]= but without something like that, trying to cleanly implement assignment in this way is just nasty, involving messy intermediary objects, caveats for the way it should be used, and performance penalties.


#20

That’s not what I wanted to hear…

But I think there is nothing wrong with returning a non const reference to the properties, so the operator= already does the job…
Also the non const version is only called, if an assignement is to be made, otherwise it would choose the const version? So it would be ok to add a var() for assigning the value to.

The only problem is, if the var is not a DynamicObject. Then you are already in the assignement, you can’t bail out…

It could be so good…