Home > database >  Detecting JSON Subschema in JavaScript
Detecting JSON Subschema in JavaScript

Time:09-24

My JSON could have "n" layers of subschema - How can I detect if a key has a subschema under it to prevent if from looping incorrectly as it's doing today? As you can see in my code below I am starting to consider moving the subschemas to arrays, but this feels like a workaround, not the correct approach. Lastly, is it possible to avoid multiple FOR loops to better handle the "n" layers of subschema.

jsfiddle

HTML

<table></table>

Javascript

var data = {
  "THREE LEVEL": {
    "PRODUCTION": {
      "One": "http://one.com",
      "Two": "http://two.com"
    },
    "LATEST": {
      "alpha": "http://alpha.com",
      "omega": "http://omega.com"
    },
    "STAGE": {
      "Apha": "http://alpha.com",
      "Bravo": "http://bravo.com", 
      "Charley": "http://charley.com"
    }
  },
  "TWO LEVEL": {
    "PRODUCTION": "http://aaa.com",
    "LATEST": "http://bbb.com",
    "STAGE": "http://ccc.com"
  }
}

var table = document.querySelector('table');
var rows = '';

for (var lev_1 in data) {
  rows  = '<tr><td>'   lev_1   '</td><td></td></tr>'
  //if(Array.isArray(data[lev_1])){
    for (var lev_2 in data[lev_1]) {
      rows  = '<tr><td><a href="'   data[lev_1][lev_2]   '">'   lev_2   '</td><td></td></tr>'
      //if(Array.isArray(data[lev_1][lev_2])) {
        for (var lev_3 in data[lev_1][lev_2]) {
          rows  = '<tr><td><a href="'   data[lev_1][lev_2][lev_3]   '">'   lev_3   '</td><td></td></tr>'
        }
      //}
    }  
  //}
}
table.innerHTML = rows;

Current Output

THREE LEVEL 
PRODUCTION  
One 
Two 
LATEST  
alpha   
omega   
STAGE   
Apha    
Bravo   
Charley 
TWO LEVEL   
PRODUCTION  
0   
1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
LATEST  
0   
1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
STAGE   
0   
1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  

CodePudding user response:

Use typeof() to determine what kind of element you're dealing with.

  • If its an object then use Object.keys() to get an array of all of the keys within the object. Then iterate through the keys, testing each with typeof()...

  • If it's an array, then iterate through each element of the array testing each with typeof()

  • Rinse and repeat.

The following example should give you a better idea of how to proceed.

var data = {
  "THREE LEVEL": {
    "PRODUCTION": {
      "One": "http://one.com",
      "Two": "http://two.com"
    },
    "LATEST": {
      "alpha": "http://alpha.com",
      "omega": "http://omega.com"
    },
    "STAGE": {
      "Apha": "http://alpha.com",
      "Bravo": "http://bravo.com",
      "Charley": "http://charley.com"
    }
  },
  "TWO LEVEL": {
    "PRODUCTION": "http://aaa.com",
    "LATEST": "http://bbb.com",
    "STAGE": "http://ccc.com"
  }
}

var table = document.querySelector('table');
var rows = '';

var iterateObject = function(dataObject) {
  var keys = Object.keys(dataObject);
  keys.forEach(function(key) {
    var textArray = [];
    var element = dataObject[key];
    switch (typeof(element)) {
      case 'object':
        textArray.push(key)
        dumpRow(textArray);
        iterateObject(element);
        break;
      case 'array':
        textArray.push(key)
        dumpRow(textArray);
        iterateArray(element);
        break;
      default:
        dumpElement(dataObject, key);
        break;
    }
  });
};

var iterateArray = function(dataArray) {
  dataArray.forEach(function(element) {
    switch (typeof(element)) {
      case 'object':
        iterateObject(element);
        break;
      case 'array':
        iterateArray(element);
        break;
      default:
        var textArray = [];
        textArray.push(element)
        dumpRow(textArray);
        break;
    }
  });
};

var dumpElement = function(obj, key) {
  var textArray = [];
  textArray.push(key);
  textArray.push(obj[key]);
  dumpRow(textArray);
};

var dumpRow = function(textArray) {
  var dumpString = '<tr>';
  textArray.forEach(function(text) {
    dumpString  = dumpCell(text);
  });
  dumpString  = '</tr>';
  rows  = dumpString;
};

var dumpCell = function(dumpText) {
  var dumpString = '<td>';
  dumpString  = dumpText;
  dumpString  = '</td>';
  return dumpString;
};

switch (typeof(data)) {
  case 'object':
    iterateObject(data);
    break;
  case 'array':
    iterateArray(data);
    break;
  default:
    var textArray = [];
    textArray.push(data)
    dumpRow(textArray);
    break;
}

table.innerHTML = rows;
<html>

<body>
</body>
<table>
</table>

</html>

CodePudding user response:

You should probably make some kind of TableMaker:

//<![CDATA[
/* js/external.js */
let doc, htm, bod, nav, M, I, mobile, S, Q, TableMaker; // for use on other loads
addEventListener('load', ()=>{
doc = document; htm = doc.documentElement; bod = doc.body; nav = navigator; M = tag=>doc.createElement(tag); I = id=>doc.getElementById(id); mobile = /Mobi/i.test(nav.userAgent);
S = (selector, within)=>{
  let w = within || doc;
  return w.querySelector(selector);
}
Q = (selector, within)=>{
  let w = within || doc;
  return w.querySelectorAll(selector);
}
TableMaker = function(object){
  this.table = M('table'); this.thead = M('thead'); this.tbody = M('tbody'); this.tfoot = M('tfoot');
  this.trMake = ()=>{
    return M('tr');
  }
  this.thMake = textContent=>{
    const th = M('th');
    th.textContent = textContent;
    return th;
  }
  this.tdMake = textContent=>{
    const td = M('td');
    td.textContent = textContent;
    return td;
  }
  if(object !== undefined){
    let tr, o, n, td;
    for(let k in object){
      tr = this.trMake();
      if(typeof k === 'string'){
        tr.appendChild(this.tdMake(k)); this.tbody.appendChild(tr); o = object[k];
        if(typeof o === 'string'){
          tr.appendChild(this.tdMake(o));
        }
        else{
          n = new TableMaker(object[k]); td = this.tdMake(); td.appendChild(n.table); tr.append(td);
        }
      }
    }
  }
  this.table.appendChild(this.thead); this.table.appendChild(this.tbody); this.table.appendChild(this.tfoot);
}
// magic below can be put on a separate page - except // end load line
var data = {
  "THREE LEVEL": {
    "PRODUCTION": {
      "One": "http://one.com",
      "Two": "http://two.com"
    },
    "LATEST": {
      "alpha": "http://alpha.com",
      "omega": "http://omega.com"
    },
    "STAGE": {
      "Apha": "http://alpha.com",
      "Bravo": "http://bravo.com", 
      "Charley": "http://charley.com"
    }
  },
  "TWO LEVEL": {
    "PRODUCTION": "http://aaa.com",
    "LATEST": "http://bbb.com",
    "STAGE": "http://ccc.com"
  }
}
const tM = new TableMaker(data);
bod.appendChild(tM.table);
}); // end load
//]]>
table{
  border:1px solid #000;
}
td{
  border:1px solid #090;
}

You'll notice I've included a small library, as well. Note the recursive nature of TableMaker.

  • Related