2
votes

I have a bunch of strings of the form: "AAA.BBB[0].CCC.DDD[5].EEE = 123". Some contain even more deeply nested arrays. How would I convert that dot notation to the equivalent JSON object? I am using jQuery if that provides any additional advantages. I've found ways to ALMOST do this, but no solution works when arrays are included.

edit: I need to do this for many strings and ultimately combine them. This one, in particular, should become an object of the form (assuming I didn't make any errors): { "AAA" : { "BBB": [ "CCC : { "DDD": [ {}, {}, {}, {}, {}, { "EEE": 123 } ] } ] }

2
What do you expect the "JSON object" to be? Do you want a JavaScript object that contains merely the objects/arrays needed to traverse that path?pimvdb
What would an "equivalent JSON object" look like? What does AAA.BBB[0].CCC.DDD[5].EEE mean?Rocket Hazmat
{ "AAA" : { "BBB": [ "CCC : { "DDD": [ {}, {}, {}, {}, {}, { "EEE": 123 } ] } ] }...there may be slight errors in that, but hopefully you'll get the point. I'll edit the question to include the = 123 part since I did forget that.Chris
I think you're mixing concepts. A path only consists of specific keys - it does not describe a complete object with values and other keys. Consequently you cannot convert the one into the other really.pimvdb
Empty objects will suffice for missing array elements and the like. I edited above to state that my collection of strings will completely describe the final object. Validation is not a concern.Chris

2 Answers

3
votes

What you're describing isn't something that can just be converted to JSON. What you're describing is the hierarchy to the object, sure, but with those braces in there, you're obviously looking at but one piece of a much larger object. However, for a parser to turn that string into a javascript object, we can do this:

function splitStringToObject(string){
  var sourceArray = string.split('.');
  var top = {};
  var point = top;
  while (sourceArray.length){
    var work = sourceArray.shift();
    if ( /([a-zA-Z_][a-zA-Z0-9_]*)\[([a-zA-Z0-9_]+)\]/.test(work) ){
      console.log('found match alpha index')
      //found an array identifier with a variable name inside ('bbb[aaa]')
      var matches = /([a-zA-Z_][a-zA-Z0-9_]*)\[([a-zA-Z0-9_]+)\]/.exec(work);
      var matchName = matches[1];
      var matchIndex = matches[2];
      point[matchName] = [];
      point[matchName][matchIndex] = {};
      point = point[matchName][matchIndex];
    } else if ( /([a-zA-Z_][a-zA-Z0-9_]*)\[([0-9]+)\]/.test(work) ) {
      console.log('found match numeric index')
      //found an array identifier with a numeric index inside ('bbb[0]')
      var matches = /([a-zA-Z_][a-zA-Z0-9_]*)\[([a-zA-Z0-9_]+)\]/.exec(work);
      var matchName = matches[1];
      var matchIndex = matches[2];
      point[matchName] = [];
      point[matchName][matchIndex] = {};
      point = point[matchName][matchIndex];
    } else if ( work.indexOf('[') > -1 || work.indexOf(']') > -1 ) {
      console.log('found bad egg with ' + work)
    } else if ( work.indexOf('=') > 0 ) { 
      console.log('found = inside')
      //test for equals sign in the string
      var morework = work.split('=');
      work = morework[0].trim();
      sourceArray.push('='+morework[1])
      point[work] = morework[1].trim();
      point = point[work];
    } else { 
      console.log('found plain word')
      //assume work is aok to use as another object here.
      work = work.trim();
      point[work] = {};
      point = point[work];
    }
  }
  //you may not want this next part
  var ret;
  //let's pull our value off the top, altho if we do this, I don't know
  //how to retain the name. I prefer to return it under top still
  for (var k in top){
    ret = top[k];
  }

  console.log(ret);
  return ret;

  // alternately call
  return top;
}

However, this is just a parser, how do we use that? Well, we need to feed it our strings. I'm going to assume you can neatly get all your strings into an array, like this:

var strings = [
  "AAA.BBB[0].CCC.DDD[1].EEE = 123",
  "AAA.BBB[0].CCC.DDD[2].EEE = 123",
  "AAA.BBB[0].CCC.DDD[4].EEE = 123",
  "AAA.BBB[0].CCC.DDD[5].EEE = 123",
];

and then the next part becomes really easy, as we see here:

var objectsConverted = [];
for(var k in strings){
  objectsConverted[k] = splitStringToObject(strings[k]);
}

var result = {};
for(var k in objectsConverted){
  jQuery.extend(true,result,objectsConverted[k]);
}

console.log(result);

and at the end of the day if I JSON.stringify(result) I get:

"{"BBB":[{"CCC":{"DDD":[null,{"EEE":"123"},{"EEE":"123"},null,{"EEE":"123"},{"EEE":"123"}]}}]}"

PLEASE read the caveat inside the end of the function, the namespace is lost because of the choice of return method (I'm returning from the top instead of nested once down)

0
votes

AAA.BBB[0].CCC.DDD[5].EEE

Becomes:

{AAA : {
  BBB : [{ 
   CCC: {
    DDD: [ 
...

If you have a member X[Y] you just need to convert it to an object mapping X to an array of elements.

Does that help?