【问题标题】:Recursive JSON Generation Function递归 JSON 生成函数
【发布时间】:2015-05-14 21:31:32
【问题描述】:

这会有点啰嗦,但这里是:

我正在编写两个独立的 Web 应用程序:一个用于生成包含大量数据和格式信息的 JSON 对象,另一个用于将该 JSON 对象转换为格式良好的 HTML 页面以供查看。我目前遇到的特殊问题是嵌套列表。

对于查看应用程序,我编写了一个递归函数,它将解析提供的 javascript 对象并为具有任意数量嵌套元素的列表创建 HTML。

function createUnorderedList(object) {
    var ul = $('<ol>');

    for (var i = 0; i < object.length; i++) {
        if (object[i].children === undefined) {
            ul.append($('<li>').text(object[i].value));
        } else {
            ul.append(
            $('<li>').html(
            object[i].value + "<ul>" + createUnorderedList(object[i].children) + "</ul>"));
        }
    }

    return ul.html();
}

该函数的 jsfiddle 可以在这里看到:jsfiddle: Recursive HTML List Generation

现在,我需要为 JSON 创建方面创建一个函数。在尝试并未能将我的大脑包裹在如何递归地执行此操作上之后,我选择了一个更线性的函数,如下所示:

function createList( array ) {
    var json = { "value": [] } ;
    var k = 0 ;
    if(array[array.length-1] === "") array.pop() ;
    for(var i=0; i<array.length; i++ ){
        json.value.push( { "value": array[i] } ) ;
        for(var j=i+1; j<array.length; j++) {
            if(array[j].charAt(0) === '\t') {
                array[j] = array[j].replace( /\t/, '' ) ;
                i++;
                if(json.value[k].children === undefined)
                    $.extend( json.value[k], { children : [] } ) ;
                json.value[k].children.push( { "value": array[j] } ) ;
            } else { k++; j=array.length; }
        }
    }
    return json ;
}

jsfiddle: JSON List Generation.

这个函数只能处理一个数量级的嵌套列表,目前应该足够了,但并不理想。如何使这个函数能够生成具有任意数量嵌套元素的 json 对象?

谢谢。

【问题讨论】:

  • 我会创建一个元组数组。对象没有排序。这可能不会给您想要的结果。
  • 另外,您可以使用Array.prototype.reduce() 来简化您的逻辑。见makeOrderedList3() in my example

标签: javascript jquery json recursion


【解决方案1】:

编辑::我一开始误解了你的问题。

这是您如何将ul 层次结构解析为对象(查看 JS 控制台以查看解析出的内容):

function itemsToHierarchy(node) {
    return $(node).find('> ul > li').toArray().map(function (item) {
        return {
            value: item.childNodes[0].textContent,
            children: itemsToHierarchy(item)
        };
    });
}

console.log(itemsToHierarchy($('#list')));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<!-- leaving the HTML scrunched up to save space -->
<div id="list"><ul><li>Item 1<ul><li>Item 1.1</li></ul></li><li>Item 2</li><li>Item 3<ul><li>Item 3.1</li><li>Item 3.2<ul><li>Item 3.2.1<ul><li>Item 3.2.1.1</li></ul></li></ul></li></ul></li><li>Item 4<ul><li>Item 4.1</li><li>Item 4.2</li></ul></li><li>Item 5<ul><li>Item 5.1</li></ul></li></ul></div>

起初我以为您是在寻求有关 createList() 功能的帮助。这是适用于任何嵌套深度的递归版本:

function getItems( array, depth ) {
    depth = depth || 0;
    var items = [],
        item = array.shift();

    while (item && countTabs(item) === depth) {
        items.push({
            value: item.replace(/^\t*/, ''),
            children: getItems(array, depth + 1)
        });
        item = array.shift();
    }

    if (item) { // not at this depth
        array.unshift(item);
    }

    return items;    
}

function countTabs(value) {
    return value.match(/^\t*/)[0].length;
}

从中返回的值可用于构建 HTML,如下所示:

function createUnorderedList(items) {
    return $('<ul>').append($.map(items, function (item) {
        var li = $('<li>').text(item.value);

        if (item.children.length) {
            li.append(createUnorderedList(item.children));
        }

        return li;
    }));        
}

完整解决方案:https://jsfiddle.net/1930t20f/

【讨论】:

  • 这个解决方案看起来应该可以工作(虽然如果没有孩子,我没有包含“孩子”元素,但现在我只是很挑剔)。谢谢您的帮助!干杯:-)
  • @SimonPaonessa 是的,我知道你在没有孩子的时候会留下未定义的孩子,上面的方法可以很容易地修改为这样做,但我认为让孩子的空数组更加一致并允许用于没有任何空值检查的更简洁的算法。
  • 哈哈。可能你是对的。就像我说的,没什么大不了的。
  • 您可以将console.log(itemsToHierarchy($('#list'))); 替换为$(document).append('&lt;code&gt;'+itemsToHierarchy($('#list'))+'&lt;/code&gt;');(或类似名称)
【解决方案2】:

经过一番深思熟虑,我找到了解决方案。

首先,我更改了您的代码,为每个值创建了一个&lt;li&gt;。孩子是&lt;li&gt;之后的&lt;ul&gt;

function createUnorderedList(object) {
    var ul = $('<ul>');
    for (var i = 0; i < object.length; i++) {
        ul.append($('<li>').text(object[i].value));
        if (object[i].children) {
            ul.append($('<ul>').html(createUnorderedList(object[i].children)));
        }
    }
    return ul.html();
}

然后我创建了一个递归搜索所有元素的函数。 &lt;ul&gt; 是之前的 &lt;li&gt; 的子代。

var createJSONRecursive = function(elem, arr) {
    var childNodes = elem.childNodes;
    var nodeTmp;
    for (var i = 0; i<childNodes.length; i++) {
        var node = {};
        var tagName = childNodes[i].tagName;
        if (tagName == 'LI') {
            node.value = childNodes[i].innerHTML;
            if (i < childNodes.length - 1 && childNodes[i+1].tagName == 'UL') {
                nodeTmp = node;
            }
            else {
                arr.push(node);
            }
        }
        else if (tagName == 'UL') {
            nodeTmp.children = [];
            createJSONRecursive(childNodes[i], nodeTmp.children);        
            arr.push(nodeTmp);
        }        
    }    
}

var createJSON = function(elem) {
    var json = {value:[]}
    createJSONRecursive(elem, json.value);
    return json;    
}

var json = createJSON(document.getElementById('div'));

这里是小提琴:https://jsfiddle.net/8qc2mcyz/2/

【讨论】:

  • 可能没什么大不了的,但此解决方案依赖于将 &lt;ul&gt;s 直接放在其他 &lt;ul&gt;s 中,这在技术上不是有效的 HTML。
  • 这不是我想要做的,但它确实很有帮助。我正在寻找从文本区域中的用户输入生成 JSON 对象,其中选项卡将指示层次结构。我在链接到的第二个 jsfiddle 中使用的字符串(本质上)是我将使用的输入。不过,你让我走上了正确的道路,所以谢谢你。干杯。 :-)
猜你喜欢
  • 2017-02-16
  • 2021-04-28
  • 2016-06-13
  • 2012-04-06
  • 2016-02-16
  • 1970-01-01
  • 2016-04-25
  • 2016-04-03
  • 2021-01-11
相关资源
最近更新 更多