0

I have a JSON object and want to traverse it so that it matches the structure of JSTree:

Original JSON:

{
 "analogBasebandProcessorBoardType": "Test_BOARD",
 "tuners": {
  "tuner1": "RFE_MAIN",
  "tuner2": "MAX93"
 },
 "allowedSoftConfigs": ["30-16", "30-29", "10-22"]
}

It should look like this:

{
    'core': {
      'data': [
      {
          'text': 'analogBasebandProcessorBoardType',
          'children': 
                      [
                        {
                          'text': 'Test_BOARD'
                        }
                      ]
      }, 
      {
          'text': 'tuners',
          'children': 
                      [{
                        'text': 'Tuner1',
                        'children': 
                        [{
                          'text': 'RFE_MAIN'
                        }]
                      },
                       {
                         'text': 'Tuner2',
                         'children': 
                         [{
                           'text': 'MAX93'
                         }]
                       }
                      ]
       },

       {
          'text': 'allowedSoftConfigs',
          'children': 
                [
                        {
                            'text': '30-16'
                         }, 
                         {
                            'text': '30-29'
                         },
                         {
                            'text': '10-22'
                         }
                ]
        },

      ]
    }
  }

I think the only way to solve this is traversing. I have tried it but it is not exactly what I want, but I think I am not very far away from the solution.

This is the JSON that is being generated:

{
 "core": {
  "data": {
   "analogBasebandProcessorBoardType": {
    "text": "analogBasebandProcessorBoardType",
    "children": [{
     "text": "Test_BOARD"
    }],
    "tuners": {
     "tuner1": {
      "text": "tuner1",
      "children": [{
       "text": "RFE_MAIN"
      }],
      "tuner2": {
       "text": "tuner2",
       "children": [{
        "text": "MAX93"
       }],
       "allowedSoftConfigs": {
        "0": {
         "1": {
          "2": {
           "text": "2",
           "children": [{
            "text": "10-22"
           }]
          },
          "text": "1",
          "children": [{
           "text": "30-29"
          }]
         },
         "text": "0",
         "children": [{
          "text": "30-16"
         }]
        }
       }
      }
     }
    }
   }
  }
 }
}

My code alway uses the "name" as the key for the data-array. It would be correct if there is no key. But I am not sure where this behavior is caused.

It would be great if someone could take a look on it as I don't know how to solve it. Here is the complete code but it is easier to view in jsfiddle i think:

//function to add something to objects with a string path
function assign(obj, prop, value) {
    if (typeof prop === "string")
        prop = prop.split(".");

    if (prop.length > 1) {
        var e = prop.shift();
        assign(obj[e] =
                 Object.prototype.toString.call(obj[e]) === "[object Object]"
                 ? obj[e]
                 : {},
               prop,
               value);
    } else
        obj[prop[0]] = value;
}



$(function() {
  // 6 create an instance when the DOM is ready

var tbjsonstring = '{ "analogBasebandProcessorBoardType": "Test_BOARD", "tuners": {  "tuner1": "RFE_MAIN","tuner2": "MAX93" }, "allowedSoftConfigs": ["30-16", "30-29", "10-22"]}';

var tbjson = JSON.parse(tbjsonstring);

var computedJSON = {
    'core': {
      'data': [
      ]
    }
  }
var path = "core.data";

console.log(tbjson);
(function traverse(o) {
var z = 0;
    for (var i in o) {
      data0 = {
                  'text': i,
             }
       data1 = {
                  'text': o[i],
             }      
      if(traversed == 1){
      console.log("traversed" + o[i]);
      // assign(computedJSON,path,data1);
        traversed = 0;
      }else{
     //   assign(computedJSON,path,data0);
      }       
     
      console.log('key : ' + i + ', value: ' + o[i]);

   //console.log(path);
   path = path+"."+i;
      z++;
      if (o[i] !== null && typeof(o[i])=="object") {
        //going on step down in the object tree!!
        var traversed = "1";
        traverse(o[i]);
      }else{
      //key value pair, no children
      data = {};
      data = {
          'text': i,
          'children': [{
            'text': o[i]
          }]
      }
      assign(computedJSON,path,data);
      }  
    }
  })
  (tbjson);

//print to the console
console.log(JSON.stringify(computedJSON));





//This is the working json, computedJSON should looke like this:
  var jstreejson = {
    'core': {
      'data': [
      {
          'text': 'analogBasebandProcessorBoardType',
          'children': 
                      [
                        {
                          'text': 'Test_BOARD'
                        }
                      ]
      }, 
      {
          'text': 'tuners',
          'children': 
                      [{
                        'text': 'Tuner1',
                        'children': 
                        [{
                          'text': 'RFE_MAIN'
                        }]
                      },
                       {
                         'text': 'Tuner2',
                         'children': 
                         [{
                           'text': 'MAX93'
                         }]
                       }
                      ]
       },

       {
          'text': 'allowedSoftConfigs',
          'children': 
                [
                        {
                            'text': '30-16'
                         }, 
                         {
                            'text': '30-29'
                         },
                         {
                            'text': '10-22'
                         }
                ]
        },

      ]
    }
  }


//jstree initialization
  $('#jstree').jstree(jstreejson);
  $('#tbjstree').jstree(computedJSON);
  // 7 bind to events triggered on the tree
  $('#jstree').on("changed.jstree", function(e, data) {
    console.log(data.selected);
  });

});
The won't run in the stackoverflow-snippet-editor (even with loaded js/css files) so please visit jsfiddle for a working demo:
<a href='https://jsfiddle.net/dnffx4g8/6/' target='_blank'>JSFiddle Demo</a>

jsfiddle-link: https://jsfiddle.net/dnffx4g8/6/

bademeister
  • 107
  • 1
  • 11

2 Answers2

1

You could use an iterative an recursive approach for it, with a function who checks for array and object and iterates accordingly.

function buildObject(source) {
    if (Array.isArray(source)) {
        return source.reduce(function (r, a) {
            if (a !== null && typeof a === 'object') {
                return r.concat(buildObject(a));
            }
            r.push({ text: a });
            return r;
        }, []);
    }
    if (source !== null && typeof source === 'object') {
        return Object.keys(source).map(function (k) {
            return {
                text: k,
                children: buildObject(source[k])
            };
        });
    }
    return [{ text: source }];
}

var data = { "analogBasebandProcessorBoardType": "Test_BOARD", "tuners": { "tuner1": "RFE_MAIN", "tuner2": "MAX93" }, "allowedSoftConfigs": ["30-16", "30-29", "10-22"], "PathValue": [{ "links": ["in1->GSES_1.in1", "GSES_1.out1->GSES_1.in1", "GSES_1.out1->out1_1"], "ParamFile": "IN1_OUT12.txt" }, { "links": ["in1->GSES_1.in1", "GSES_1.out2->GSES_2.in1", "GSES_2.out1->out1_2"], "ParamFile": "IN1_OUT52.txt" }] },
    result = { core: { data: buildObject(data) } };

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

ES6

function buildObject(source) {
    if (Array.isArray(source)) {
        return source.map(text => ({ text }));
    }
    if (source !== null && typeof source === 'object') {
        return Object.keys(source).map(text => ({ text, children: buildObject(source[text])}));
    }
    return [{ text: source }];
}

var object = { analogBasebandProcessorBoardType: "Test_BOARD", tuners: { tuner1: "RFE_MAIN", tuner2: "MAX93" }, allowedSoftConfigs: ["30-16", "30-29", "10-22"] },
    result = { core: { data: buildObject(object) } };
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Thank you for the input. The non-ES6-approach works best, but it does not work with objects in objects. I have updated the JSFiddle: – bademeister Oct 13 '16 at 09:40
  • https://jsfiddle.net/dnffx4g8/7/ The ES6-variant does not show childrens of a children. For compability-reasons I would prefer the non-ES6 variant but I don't know how to modify it that it works with objects. Could you help me out again? – bademeister Oct 13 '16 at 09:42
  • @bademeister, please see edit in the first proposal. – Nina Scholz Oct 13 '16 at 10:25
  • 1
    Great, now it works as expected, thank you! I wonder that no one else had this problem with jsTree and own JSON-objects. – bademeister Oct 13 '16 at 12:16
  • "PathValue": [{ "links": ["in1->GSES_1.in1", "GSES_1.out1->GSES_1.in1", "GSES_1.out1->out1_1"], "ParamFile": "IN1_OUT12.txt" }, { "links": ["in1->GSES_1.in1", "GSES_1.out2->GSES_2.in1", "GSES_2.out1->out1_2"], "ParamFile": "IN1_OUT52.txt" }]
    This multi dim array of object is flatten to a single array. Is there a way to keep multi dimension array?
    – user2618844 Jun 18 '17 at 11:58
0

Given an object named data, you could use this ES6 function:

var result = {
  core: {
    data: (function treeify(data) {
        return Array.isArray(data) ? data.map ( value => treeify(value) )
            : typeof data !== 'object' || data == null ? { text: data }
            : Object.keys(data).map(key => ({ text:key, children: [treeify(data[key])] }));
    })(data)
  }
};

Here is a snippet with your sample data:

var data = {
 "analogBasebandProcessorBoardType": "Test_BOARD",
 "tuners": {
  "tuner1": "RFE_MAIN",
  "tuner2": "MAX93"
 },
 "allowedSoftConfigs": ["30-16", "30-29", "10-22"]
};

var result = {
  core: {
    data: (function treeify(data) {
        return Array.isArray(data) ? data.map ( value => treeify(value) )
            : typeof data !== 'object' || data == null ? { text: data }
            : Object.keys(data).map ( key => ({ text: key, children: [treeify(data[key])] }) );
    })(data)
  }
};

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

NB: I would not name your objects tbjson or computedJson, to avoid confusion with plain JavaScript objects. The term JSON is better reserved for the text notation.

Explanation of the Code

treeify is a recursive function. The value it returns depends on the data type of the argument that is passed to it:

  • When it is an array (Array.isArray()) then the function is called recursively for each element (treeify(value)), and these results are assembled in a new array (the return value of data.map()) that is returned to the caller.

  • When it is a non-object (as null is considered an object a separate test is needed for that), then the value is given to the text property of a new object which is returned to the caller ({ text: data }).

  • When it is an object, the keys of that object are iterated (Object.keys(data).map()) and for each corresponding value the function is called recursively (treeify(data[key])). That result is put in an array and assigned to the children property of a new object, while the key is assigned to the text property of that same object. This is returned to the caller.

The result variable will start the calling chain for initialising the value of the data property.

trincot
  • 317,000
  • 35
  • 244
  • 286