Storing other objects in cJobject

If cJobject was just about getting stuff in and out of JSON, then we’d stop here, but you’ll find that I use it in pretty much all the VBA examples on this site. This gives me the ability to deal with  structured data in VBA in the same way as you can in more modern languages, and also gives me an easy migration path from (and occassionally to) VBA. In fact, although I don’t use it much nowadays, I implemented a cJobject in JavaScript  so that I could port projects to Google Apps Script with minimal changes.
So this means that I needed to store any object (not just stringifiable ones) as the value of a key/value pair in a cJobject.

Example

For the sake of this example, we’ll populate a sheet and store some Excel Sheet objects in a cJobject. Normally I don’t need to do this since I always use data abstraction when interacting with sheets (see  How to use cDataSet) and that has built in methods for converting to and from cJobjects and JSON, but for the sake of an example, let’s try this. We’ll use the test data from cJobject deep dive and write it to a sheet.


First the headings – we’ll call the columns the same thing as the keys of the JSON data.

Dim r As Range
    Set r = Range("Sheet1!a1")
    
    ' write the headings
    For Each jm In job.children(1).children
        r.Offset(, jm.childIndex - 1).value = jm.key
    Next jm

Now the data

For Each jp In job.children
        For Each jm In jp.children
            r.Offset(jp.childIndex, jm.childIndex - 1).value = jm.value
        Next jm
    Next jp

That correctly gives us this

But there’s no guarantee that the properties of each of the objects in the array are each in the same order if they came from some external JSON source, so we need to refine things a little.

Storing objects as values

Now we’ll create a new cJobject in which we store the Excel Range object at which we stored each column heading. Each property in this cJobject will have a key of the column name. Like this we can retrieve the address of the column header later when we process the data

 ' write the headings
    Set headings = New cJobject
    With headings.init(Nothing)
        For Each jm In job.children(1).children
            headings.add jm.key, r.Offset(, jm.childIndex - 1)
            headings.getObject(jm.key).value = jm.key
        Next jm
    End With

Explanation

When we use the .add method, cJobject will notice that its being asked to store an object rather than a value and treat it specially internally. Here’s what headings looks like when stringified.

{"name":"object:Range","age":"object:Range"}

Of course an Excel range is not stringifiable to JSON, so instead it reports its type against the value. Note that you can store any kind of object or custom class – even another cJobject. It will treat them all the same way.


There are a few methods specially for dealing with objects stored this way –


These are equivalent

set obj = job.child("name").getObject()
set obj = job.getObject("name")

as is

if (job.child("name").isObjValue) then...

and

if (job.isObjValue("name")) then ....

Modifying an object value

When you use .add it replaces the current property value, or creates a new one as appropriate. Objects are the same as regular values. This examples will all create or replace the properties mentioned

job.add "key", value
job.add "key.subkey", value
job.add "key", someobject

Getting back to the example

So now we have the ranges that we stored the columns names in, and all the properties of that object are open to us. 


Let’s take a look

For Each jp In headings.children
        Debug.Print jp.key, jp.getObject().Address
    Next jp

gives

>name       $A$1
age        $B$1

That means we can use the keys in the data to find the correct column to store each data value against by referencing the range object stored in the cJobject

 ' now the data
    For Each jp In job.children
        For Each jm In jp.children
            headings.getObject(jm.key).Offset(jp.childIndex).value = jm.value
        Next jm
    Next jp

which gives us the correct result

Shuffle

To test, we’ll shuffle the order of one of the objects in the test data like this

[{"name":"john","age":25},{"age":50,"name":"mary"} ]

In our first example, the data for mary would have ended up in the wrong column, but since we are now storing the column range keyed by property name, we’ll always end up in the right place regardless of the incoming order. And here’s the result

Ad hoc classes

Using cJobject means that I can get many of the benefits of classes without bothering to make a class, as well as JSON and recursion by default for every data structure and the ability to create objects on the fly. 

Now let’s look at some more cJobject topics.

For more on this see How to use cJobject