Parsing nested JSON arrays of JSON objects

I have the following JSON object that has been read from a url stream:

**{“chatId”:“a74G5k329”,“initialMessage”:{“explanation”:“I chose the chords A, E, D, and A for a standard rock progression. These chords are commonly used in rock music and create a powerful and energetic sound. The progression follows a I-IV-V-I pattern, which is a classic choice for rock music. The key of A Major provides a good balance between brightness and richness. Enjoy rocking out with these chords!”,“midiDataUri”:“data:audio/midi;base64,TVRoZAAAAAYAAAABAIBNVHJrAAACnQD/UQMHoSAA/wYFQSAoSSkAkC1AAJAtQACQMUAAkDRAAJA5QACQPUAAkEBAAJBFQACQSUCEAIAtQACALUAAgDFAAIA0QACAOUAAgD1AAIBAQACARUAAgElAAP8GBUUgKFYpAJAoQACQKEAAkCxAAJAvQACQNEAAkDhAAJA7QACQQEAAkERAhACAKEAAgChAAIAsQACAL0AAgDRAAIA4QACAO0AAgEBAAIBEQAD/BgZEIChJVikAkCZAAJAmQACQKkAAkC1AAJAyQACQNkAAkDlAAJA+QACQQkCEAIAmQACAJkAAgCpAAIAtQACAMkAAgDZAAIA5QACAPkAAgEJAAP8GBUEgKEkpAJAtQACQLUAAkDFAAJA0QACQOUAAkD1AAJBAQACQRUAAkElAhACALUAAgC1AAIAxQACANEAAgDlAAIA9QACAQEAAgEVAAIBJQAD/BgVBIChJKQCQLUAAkC1AAJAxQACQNEAAkDlAAJA9QACQQEAAkEVAAJBJQIQAgC1AAIAtQACAMUAAgDRAAIA5QACAPUAAgEBAAIBFQACASUAA/wYFRSAoVikAkChAAJAoQACQLEAAkC9AAJA0QACQOEAAkDtAAJBAQACQRECEAIAoQACAKEAAgCxAAIAvQACANEAAgDhAAIA7QACAQEAAgERAAP8GBkQgKElWKQCQJkAAkCZAAJAqQACQLUAAkDJAAJA2QACQOUAAkD5AAJBCQIQAgCZAAIAmQACAKkAAgC1AAIAyQACANkAAgDlAAIA+QACAQkAA/wYFQSAoSSkAkC1AAJAtQACQMUAAkDRAAJA5QACQPUAAkEBAAJBFQACQSUCEAIAtQACALUAAgDFAAIA0QACAOUAAgD1AAIBAQACARUAAgElAAP8vAA==”,“chords”:[{“name”:“A”,“romanNumeral”:“I”},{“name”:“E”,“romanNumeral”:“V”},{“name”:“D”,“romanNumeral”:“IV”},{“name”:“A”,“romanNumeral”:“I”},{“name”:“A”,“romanNumeral”:“I”},{“name”:“E”,“romanNumeral”:“V”},{“name”:“D”,“romanNumeral”:“IV”},{“name”:“A”,“romanNumeral”:“I”}],“bpm”:120,“key”:“A Major”,“durationInSeconds”:16}}

Here is the code I’m using to parse the object:

const juce::String stringResponse = stream->readEntireStreamAsString();
juce::String decoded = “False”;

        int arsize;
        
        if( juce::JSON::parse(stringResponse, parsedResponse).wasOk() ) {
            juce::String midiData = parsedResponse["initialMessage"]["midiDataUri"];;
            midiBase64 = midiData.replace("data:audio/midi;base64,","");
            if(juce::Base64::convertFromBase64(midi, midiBase64)){
                decoded = "True";
            }
            juce::String chatExplanation = parsedResponse["initialMessage"]["explanation"];
            juce::Array chatChords = parsedResponse["initialMessage"].getProperty("chords",juce::var()).getArray();
            if (chatChords.isEmpty()){
                std::cout << "nothing here" << std::endl;}
            else{
                std::cout << "something here" + std::to_string(chatChords.size()) << std::endl;
            };

I’m a little new to working with JUCE and C++. I can see that chatChords is an array with a single element, but I was expecting it to be an array with 8 JSON objects. Can someone help me figure out what I am doing wrong?

Take a look at Jim’s repo to help your debugger get more visibility into the juce vars.

It should make it much easier to work with dynamic objects like var.

Hi Adam, This was really helpful. I now am able to see that chatChords does indeed have what I want. But when I try this code:
juce::var firstChord = chatChords.getFirst();
I get a compiler error that I don’t understand
Conversion function from ‘juce::Array<juce::var, juce::DummyCriticalSection, 0> *’ to ‘juce::var’ invokes a deleted function
I’ve tried every juce:: type I can think of and I get the same error. Any idea what’s going on?

Is the var holding a dynamic object?

https://docs.juce.com/master/classvar.html#a60a6775a9911e0047b1ac84322eac07a

Don’t know much about it but it looks JSON related…

When you quote a compiler error, please mark in your code, where the error occurred.

I am assuming that the deleted function is a copy constructor or assignment operator.

Note: getArray() returns a pointer, because it is not necessarily an array. I would use this check:

if (!parsedResponse.isDynamicObject())
{
    DBG ("not an object");
    return;
}
auto* object = parsedResponse.getDynamicOpject();
// ...
if (object->hasProeprty ("initialMessage"))
{
    auto& initialMessage = object->getProperty ("initialMessage");
    if (auto* chatChords = initialMessage->getProperty("chords", juce::var()).getArray())
    {
        for (auto& item : *chatChords)
        {
            if (auto* chord = item.getDynamicObject())
            {
                DBG (chord->getProperty ("name").toString() << " (" << chord->getProperty ("romanNumeral").toString() << ")");
           }
        }
    }
}

And so on. Hope that helps

1 Like

Hey Daniel, you are the bomb! Thanks so much for this. I’m going to look up how deleted functions happen so I won’t make the same mistake in the future.

For those pointery classes where you have to do a nullptr check at every line, I like to make a little class/set of functions to raise exceptions/return Results when things go wrong.

For example, if you wrap a var tree in your own class, you could override the [ ] operator to perform a getProperty() and do the nullptr/void var check. You could also override the > to do the getDynamicObject() and check for nullptr.

Do bear in mind the operator precedence if you use this, though.