【问题标题】:recursively extend json object递归扩展json对象
【发布时间】:2013-12-02 21:51:06
【问题描述】:

我有一个递归 json 结构,其中包含一些属性和一个包含子节点的节点,其格式与所需的深度相同。下面的例子并不完整或与我的数据相同,但它显示了粗略的结构。

[{"page":{
    title: "my title 1",
    children: [
        {"page":{
            title: "my title 2",
            children: [ ... ]
        },{"page":{
            title: "my title 3",
            children: [ ... ]
        },{"page":{
            title: "my title 4",
            children: [
                {"page": {
                    title: "my other title",
                    children: [ ... ]
                }]
        },{"page":{
            title: "my title 5",
            children: [ ... ]
        }]
        ... etc

我想扩展对象,以便每个“页面”对象都获得一组新属性 - 例如假设我想将{score: 5.3, theme:"moody"} 添加到每个“页面”,以便它们都获得属性,这样如果我要对 json 对象进行字符串化,每个页面都会添加这些属性。

{"page":{title:"some title", score: 5.3, theme: "moody"}}

有没有办法递归扩展 json/object 结构,最好是在 jQuery 中?

【问题讨论】:

  • 用我们可以玩的实际有效对象创建一个小提琴!

标签: javascript jquery json recursion


【解决方案1】:

首先,您拥有的是一个对象数组。数组是原生 JS 对象(实际上是Object 的增强实例)。 JS 中没有 JSON 对象 这样的东西,因为 JSON 是 JavaScript Object Notation 的首字母缩写。
它是一种经常用于传输数据的格式,但格式良好的 JSON 是格式良好的 JS 文字表示法。如此之多,以至于您可以只 eval 一个 JSON 字符串,但 无论您做什么,都不要More details can be found here

尽管如此,你得到的任何解决方案都将是 JavaScript 解决方案,因为 jQ 就是 JavaScript……你在 JS 中所做的一切都在 jQ 中工作,你编写的每条涉及 jQ 的语句都会评估一系列函数调用和构成 jQ 工具包的其他魔法,无论如何,所有这些都是用普通的 'ol VanillaJS 编写的。

但是,回到你的问题。看看你的结构,我想我说每个 children 属性都是一个数组,包含对象,(可能)有一个 page 属性。该属性将有一个分配给它的对象。这些都是我的假设,但我的印象是这里就是这种情况。这意味着,要扩展这个对象,一个简单的 map-callback-function 就足够了:

yourArray = yourArray.map(function ext(obj)
{//named callback function, does not pollute global scope
   obj.title = "title";
   obj.score = 5.3;
   if (obj.children instanceOf Array)
   {//recursively apply callback
       obj.children = obj.children.map(ext);//reference to current callback function
   }
   return obj;//return altered object
});

这会映射每个数组,为对象分配属性,然后检查是否有 children 属性,这也是一个数组,并应用相同的回调函数来映射该数组,从而将此映射呈现为递归映射.
因为我们在这里处理对象,return obj 语句实际上是可选的,因为对象是引用:

yourArray.map(function ext(obj)
{//no assignment
   obj.title = "title";
   obj.score = 5.3;
   if (obj.children instanceOf Array)
   {
       obj.children = obj.children.map(ext);
   }
});

同样有效
使用 jQuery 的 extend 方法,您可以稍微缩短这段代码,但不会缩短很多(实际上是 1 行),当然:

yourArray = yourArray.map(function ext(obj)
{
   $.extend(obj, {title: 'title',score: 5.3});
   if (obj.children instanceOf Array)
   {
       obj.children = obj.children.map(ext);
   }
   return obj;
});

或者,更jQ的味道:

$.each(yourArray, function ext(i, obj)
{
    $.extend(obj, {title: 'title', score: 5.3});
    if (obj.children instanceof Array)
    {
        $.each(obj.children(ext));
    }
});

但正如您所见,就可读性和代码紧凑性而言,差异很小。说实话,jQ 在这里所做的一切真的是让你慢下来。
因为$.extend 需要一个对象作为第二个参数,所以我必须在每次调用回调时构造一个对象字面量。我不能使用分配给位于更高范围内的变量的对象,因为如前所述:永远不会复制对象,只有对它们的引用。使用更高范围的变量会使代码更容易出错:

var extendObj = {title: 'title', score: 4};
yourArray = yourArray.map(function ext(obj)
{
   $.extend(obj, extendObj);
   if (obj.children instanceOf Array)
   {
       obj.children = obj.children.map(ext);
   }
   return obj;
});

这会很完美,但是

var extendObj = {title: 'title', score: 4, deep:{another: 'object'}};

需要深度克隆:

$.extend(true, obj, extendObj);

如果您忘记/忽略这一点,通过一个实例更改 deep 的属性,将意味着所有其他实例也反映相同的更改...不理想

【讨论】:

  • +1 这是一个很好的彻底答案,但我认为您可能想改写第一段中的一些内容以使其更具可读性。此外 - JSON 不仅仅是首字母缩写词 - 它是一种具有规范的格式,并非所有 JS 对象文字都是有效的 JSON 对象。例如[1,2,3,] 是一个有效的 JS 数组对象字面量,但不是一个有效的 JSON 数组。
  • @BenjaminGruenbaum:对的。我稍微扩展了开头段落,澄清了一两件事,添加了指向 json.org 的链接。我现在要去吃午饭了,但我希望通过编辑,这个答案对所有人来说都足够清楚......
  • 这是一个很好的答案。感谢您解释不同的方法。
猜你喜欢
  • 2014-11-08
  • 1970-01-01
  • 1970-01-01
  • 2010-11-20
  • 2020-10-05
  • 1970-01-01
  • 2017-03-26
  • 2020-08-04
  • 2019-09-03
相关资源
最近更新 更多