MJS object structure for nested JSON

#1

If you are asking a question, please follow this template:

  1. My goal is:
    Trying to create a json object in the format state.container[variable].type = value to update a Shadow using dynamic variables.
    e.g. state: {“connected”: true, “container”: {“30AEABCD” : {“type” : 12}}}

  2. My actions are:
    In my mjs, I’m initialising state object as follows:
    let state = { connected: true, container: {} }

However, I want to dynamically update my Shadow by setting for example:
state.container[Mac].type=12
If I simply do this, it works (but isn’t what I’m looking for):
state.continaer[Mac] = 12

  1. The result I see is:
    I get an MJS error: MJS callback error: unsupported object type

  2. My expectation & question is: Please can somebody help me properly initialise the state object to allow me to dynamically add in the type for a specific Mac variable?

#2

I’m afraid you can’t do that.
2 ideas:

let state = {connected: true, container: {}};
state.container.obj = {mac: "01234567890A", type: 12};

Or create the json string in C:

const char* create_state(bool connected, const char* mac, int type){
  static char state[64];
  struct json_out jsout = JSON_OUT_BUF(state, sizeof(state));
  json_printf(&jsout, "{connected: %B, container: {%Q: {type: %d}}}",
              connected, mac, type);
  return state;
}

In mJS:

let create_state_string = ffi('char* create_state(bool, char*, int)');
let state_string = create_state_string(true, "234567890AB", 12);
let state = JSON.parse(state_string);
print('state from C: ' + JSON.stringify(state));

Output:

state from C: {"container":{"234567890AB":{"type":12}},"connected":true}
#3

Thanks @nliviu. I managed to find a sort of workaround, by doing the following:
let sub_state = {};
sub_state.type = 12;
state.container[mac] = sub_state;

When I call shadow.update(0,state) it correctly adds my desired structure to the JSON. However I also then want to be able to check the currently stored type for a given device by calling something like:
let t = state.container[Mac].type;
Which doesn’t work.

What I’m trying to achieve is a way to save 4-5 variables for a number of child devices in my device shadow, each identified by their Mac address.

Is there a better way to structure my JSON to achieve this goal, whilst still allowing me to use a JS object to read/write the values within my mjs code?

#4

I think I might have almost answered my own question: Using a JS checker, this seems to work correctly:

let mac = "30AEABCD";
let compartment = {};
compartment[mac] = {online:true};
if (isEmpty(compartment[mac])) {
  console.log("creating new object");
  compartment[mac] = {}
}
compartment[mac].online=false;
compartment[mac].type = 12;

function isEmpty(obj) {
  for (var x in obj) {
   if (obj.hasOwnProperty(x)) return false;
  }
  return true;
}

console.log(JSON.stringify(compartment[mac]));
console.log(compartment[mac].type);

However, I don’t think mjs can do obj.hasOwnProperty(x), or perhaps the issue is with the for (var in obj) part. Either way, I’m struggling to work out how to check whether compartment[mac] has been defined so that I can initialise it only if necessary without wiping out previous data