cJobject Class in Google Apps Script

The Rest to Excel library is a set of classes and modules that simplify the populating of Excel sheets with data from RestFul APIs. Google Apps Script of course already has plenty of much better ways to deal with this but,  but the objective here is to manage the transition from Excel to Google Docs, and to be able to use the same library definitions and code for both platforms.

Required conversions

In addition to the Rest to Excel library and associated examples and modules, we also need the Data manipulation Classes in Google Apps Script, already converted to GAS as part of the Roadmapper migration , and a bunch of utilities such as Optimizing access to sheet values , VBA collection in javaScript and various other hacks covered in From VBA to Google Apps Script.

The big conversion though is going to be the cJobject class, which was implemented in VBA to allow for Excel JSON conversion.

cJobject approach

The complex part of the cJobject implementation in VBA was the jSon parser. I won't need to reimplement that, since GAS has inbuilt jSon management (it is javaScript after all), but I still want to be able to use cJobject, since all the code that will be converted over will be using that object. Since the GAS jSon parser is all about populating native javaScript objects with jSON and visa versa, then a useful trick would be to be able to covert cJobjects to and from native javaScript objects. That way, all the existing code that uses cJobject can co-exist and still allow for the use of the inbuilt GAS capabilities. 

cJobject to Native

Since the cJobject, is after all, an attempt to make VBA look like javaScript whilst handling recursive objects, the conversion is really not too complex. Let's take a look at how to covert a cJobject to a native javaScript object heirachy. It is of course recursive, given the unstructured depth - but it is rather concise and works very nicely.

cJobject.prototype.toNative = function(optParent) {
  var parent = fixOptional(optParent,{});
  this.toNativeRecurse(parent);
  return parent;
};
cJobject.prototype.toNativeRecurse = function(parent){
  // converts a cJobject to a javaScript object
  var self = this;
  if (self.hasChildren()) {
    parent= parent[self.key()]= self.isArrayRoot () ? [] : {};
    self.children().forEach(
      function (item) {
         if (item.key()) {
           branch = item.toNativeRecurse(parent);
           if (item.isArrayMember()) {
             DebugAssert ( isArray (parent),self.key() + ' should have been an array');
             var p = {};
             p[item.key()]=branch;
             parent.push (p);
           }
           else {
             parent[item.key()]= branch;
           }
         }
         else {
           DebugAssert ( isArray (parent),self.key() + ' should have been an array');
           DebugAssert ( item.isArrayMember (parent),self.key() + ' has non-array members');
           parent.push(item.toNativeRecurse());
         }
       }
    );
    return parent;
  }
  else {
     return self.value();
  }

};

Serializing a cJobject

This is now a cinch. Just convert it to a javaScript native object as above, and use GAS jSon functions to stringify it. Nice, but we don't have the beautifier available as I had implemented in VBA.

cJobject.prototype.serialize = function(optBeautify){
  // beautify not implemented as im using the built in JSONifier
  // the VBA version uses my own.
  return JSON.stringify(this.toNative());
};

Native to cJobject

Coming the other way - creating a cJobject from a javaScript native object structure is a little more tricky, but still the same recursive approach, and is even more concise.

cJobject.prototype.fromNative = function(native) {
    // take a native jscript object and convert it to a cjobject
    var self = this;

    // if its an array then create an array root, and recurse with each array element
    if (isArray(native)) {
        self.addArray();
        for (var j = 0; j < native.length;j++) {
            self.add().fromNative(native[j]);
        }
    }
    // if its an object, then add each key as the next lower cjobject
    else if (IsObject(native)) {
        for (k in native) {
            self.add (k).fromNative(native[k]);
        }   
    }
    // finally we get to the value;
    else {
        self.xValue = native;
    }
    return self;
};

Deserializing a cJobject from a jSon string

It's simply converting jSon to a native javaScript object, then converting that native object to a cJobject as above.

cJobject.prototype.deSerialize = function(s) {
    // the VBA version uses its own jSON parser. This one will use the JSON.parse built in
    // simply we just parse it to native jscript object, then convert it to a cJobject
    return this.fromNative(JSON.parse(s));
};
 
Why not join our forum,follow the blog or follow me on twitter to ensure you get updates when they are available.

Transitioning is covered more comprehensively in my my book, Going Gas - from VBA to Apps script, available All formats are available now from O'Reilly,Amazon and all good bookshops. You can also read a preview on O'Reilly.
 

Comments