Convert HTML DOM structure to JSON

I spent so much time on this ... the recursive part is pretty ghostly.
for a given HTML structure, of unknown depth, I need to convert to JSON.
(I use this from some YAML i18n translation system that I create)

my general idea is to go deeper until I find INPUT , and then create an object with the key / value span.innerHTML/input.value and return that value to the object, so the value VALUE KEY, which is the last <span class="title"> , will be reached <span class="title"> .

(Yes, it's a little complicated, but very interesting to develop)

JSBIN Playground - Live Code Example

I can't get my recursive function to work correctly to output the JSON I want ...

HTML structure

 <ul> <li> <span class="title">footer</span> <ul> <li> <span>statement</span> <input type="text" value="xxx"> </li> </ul> </li> <li> <span class="title">landing</span> <ul> <li> <span>page_title</span> <input type="text" value="yyy"> </li> <li> <span>page_sub_title</span> <input type="text" value="xxx"> </li> <li> <span class="title">pricing</span> <ul class="level11"> <li> <span>title</span> <input type="text" value="aaa"> </li> <li> <span>cost</span> <input type="text" value="xxx"> </li> </ul> </li> </ul> </li> </ul> 


(Required) JSON Output

 { footer : { statement : 'xxx' }, landing : { page_title : 'yyy', page_sub_title : 'xxx', pricing : { title : 'aaa', cost : 'xxx' } } } 
+8
json javascript html
Nov 03 2018-11-11T00:
source share
4 answers

If you can convince yourself of using jQuery, try this :

 function helper(root) { var result = {}; $('> ul > li > span', root).each(function () { result[$(this).text()] = $(this).hasClass('title') ? helper($(this).parent()) : $(this).next('input').val(); }); return result; } console.log(helper('body')); 
+5
Nov 03 '11 at 10:52
source share

I am new here and I could not find how to post a comment. I wanted to ask you if this is always a structure, regardless of department. If the answer is no, then do not read my answer :).

So, first of all, I added the getPrevious function, because directly trying to get the previous sibling returns you the text node. Then I changed the recursion a bit, because it is not a simple recursion, the json format (parent-child relationship) is different from the html format. I tried it on 2 more levels, and everything is in order. Hope this is helpful and sorry if it is not.

  function getPrevious(element) { var prev_el = element.previousSibling; while (prev_el.nodeType == 3) { prev_el = prev_el.previousSibling; } return prev_el; } function recursive(element){ //var classname = element.className.split(' '); // element.nodeName == 'UL' var Result = {"title": '', "json": {}}; var json = {}; var cur_json_key = ''; if( element.nodeType == 3 ) return; else{ //console.log( element.nodeType, element ); var nodeName = element.nodeName.toLowerCase(); var nodeClass = element.className.toLowerCase(); // if this is the SPAN with class 'TITLE', then create an object with the innerHTML as KEY // and later the value should be another object, returned from the recursion... if( nodeName == 'span' && nodeClass == 'title' ){ json[element.innerHTML] = {}; Result.title = element.innerHTML; Result.json = json; } else if( nodeName == 'input' ){ // if this is an INPUT field, then the SPAN sibling before it is the KEY. var key = getPrevious(element).innerHTML; var val = element.value; Result.json[key] = val; } else { var is_title_found = 0; var title_found = ''; var res = {} // go deeper for( var child=0; child < element.childNodes.length; child++ ){ //json = $.extend( {}, recursive( element.childNodes[child] )); res = recursive( element.childNodes[child]); if (res) { if (res.title != '') { is_title_found = 1; title_found = res.title; } else { $.extend(true, json, res.json); } console.log(JSON.stringify(json)); } } if (title_found) { Result.json[title_found] = json } else { Result.json = json; } } return Result; } } 
+9
Nov 03 '11 at 11:52
source share
 <section id="in"> <ul> <li><div>lorem</div></li> <li> <div>lorem</div> <ul> <li><div>lorem</div></li> <li> <div>lorem</div> </li> <li> <div>lorem</div> <ul> <li><div>lorem</div></li> <li> <div>lorem</div> </li> <li><div>lorem</div></li> <li><div>lorem</div></li> </ul> </li> <li><div>lorem</div></li> </ul> </li> <li><div>lorem</div></li> <li><div>lorem</div></li> </ul> </section> <textarea id="outjson"></textarea> var a = []; getJSON($('#in'), a); function getJSON(el, arr) { el.children().each(function() { arr.push({}); arr[arr.length-1][this.tagName] = []; if ($(this).children().length > 0) { getJSON($(this), arr[arr.length-1][this.tagName]); } }); } $('#outjson').text(JSON.stringify(a)); 

You'll get:

[{"UL": [{"L." : [{"DIV": []}]}, {"L." : [{"DIV": []}, {"UL": [{"L.I.": [{"DIV": []}]}, {"L.": [{"DIV": [ ]}]}, {"L.": [{"DIV": []}, {"UL": [{"L.": [{"DIV": []}]}, {"L." : [{"DIV": []}]}, {"L.": [{"DIV": []}]}, {"L." : [{"DIV": []}]}]}]}, {"L." : [{"DIV": []}]}]}]}, {"LI": [{"DIV": []}]}, {"L." : [{"DIV": []}]}]}]

+1
Aug 29 '15 at 15:50
source share

Living example

 var ul = document.body.firstElementChild; // cheat to only extract the value (key is undefined) var data = extractKeyValue({}, ul)[1]; function extractKeyValue(span, thing) { // return key & input value if (thing.tagName === "INPUT") { return [span.textContent, thing.value]; } else { // recurse over every li and return the key/value of the span + thing var obj = {}; [].forEach.call(thing.children, function (li) { var span = li.firstElementChild; var thing = span.nextElementSibling; // tuple is [key, value] var tuple = extractKeyValue(span, thing); obj[tuple[0]] = tuple[1]; }); return [span.textContent, obj]; } } 
0
Nov 03 '11 at 11:51
source share



All Articles