限制 JSON 字符串化深度

Posted

技术标签:

【中文标题】限制 JSON 字符串化深度【英文标题】:Limit JSON stringification depth 【发布时间】:2013-05-04 04:35:30 【问题描述】:

当使用JSON.stringify(或类似的东西)对对象进行字符串化时,有一种方法可以限制字符串化深度,即只进入对象树的 n 层并忽略之后的所有内容(或者更好:在其中放置占位符,表示遗漏了某些内容)?

我知道JSON.stringify 采用function (key, value) 形式的替换函数,但我没有找到一种方法来获取传递给替换函数的当前键值对的原始对象的深度。

有没有办法使用默认的 JSON.stringify 实现来做到这一点?还是我已经到了应该自己实现字符串化的地步? 或者您可以推荐另一个具有此选项的字符串化库?

【问题讨论】:

看起来这个问题的已接受答案可能会有所帮助:***.com/questions/13861254/… @Strille 嗯,它确实包含一个实现我想要的。我接受它,这意味着默认实现是不可能的?我宁愿使用默认实现,因为我认为它更快,因为它是本机实现的。但是感谢您的指点! 不,我很确定默认实现无法满足您的要求。 【参考方案1】:

这是一个尊重内置 JSON.stringify() 规则同时也限制深度的函数:

function stringify(val, depth, replacer, space) 
    depth = isNaN(+depth) ? 1 : depth;
    function _build(key, val, depth, o, a)  // (JSON.stringify() has it's own rules, which we respect here by using it for property iteration)
        return !val || typeof val != 'object' ? val : (a=Array.isArray(val), JSON.stringify(val, function(k,v) if (a || depth > 0)  if (replacer) v=replacer(k,v); if (!k) return (a=Array.isArray(v),val=v); !o && (o=a?[]:); o[k] = _build(k, v, a?depth:depth-1);  ), o||(a?[]:));
    
    return JSON.stringify(_build('', val, depth), null, space);

它是如何工作的:

    _build() 被递归调用以将嵌套对象和数组构建到请求的深度。 JSON.stringify() 用于迭代每个对象的直接属性以遵守内置规则。 'undefined' 总是从内部替换器返回,因此实际上还没有构造任何 JSON。请记住,第一次调用内部替换器时,键是空的(这是要字符串化的项目)。 在最终结果上调用 JSON.stringify() 以生成实际的 JSON。

例子:

var value=a:[12,2,y:3,z:q:1],s:'!',o:x:1,o2:y:1;

console.log(stringify(value, 0, null, 2));
console.log(stringify(value, 1, null, 2));
console.log(stringify(value, 2, null, 2));




  "a": [
    12,
    2,
    
  ],
  "s": "!",
  "o": 



  "a": [
    12,
    2,
    
      "y": 3,
      "z": 
    
  ],
  "s": "!",
  "o": 
    "x": 1,
    "o2": 
  

(有关处理循环引用的版本,请参见此处:https://***.com/a/57193345/1236397 - 包括 TypeScript 版本)

更新:修复了空数组呈现为空对象的错误。

【讨论】:

为每个数组条目打印空白不是很好 不确定你的意思。它根本不会为数组打印空白,我只是再次检查。 想象“a”包含所有对象(而不是 12 和 2 以及一个对象)。如果“a”有 500 个这样的对象,您会看到打印出 500 个空括号(深度为 1)。信息量不大。 这是设计使然。数组元素的深度为 1,而不是它们包含的对象。由于原始值不被视为嵌套对象,因此包含在内。数组中的任何对象都不能在属性设置为 undefined 的情况下输出 - 这是您自己的自定义要求。如果您想要具有属性的对象,请增加您的深度级别以包含这些属性及其原始值。一切都按预期进行。 作为记录,C# 在使用 Newtonsoft.Json 时对空对象执行相同的操作。当有空对象时,它输出 。该库在序列化期间不支持深度限制,但如果支持,您很可能会得到相同的结果,或者为 null)。在 JS 中,如果需要,很容易检测空对象。如果您想要 null 代替,则将此 a?[]: 更改为此 a?[]:null【参考方案2】:

我想在不包含第三方库/太多代码的情况下在第一级对对象进行字符串化。

如果您也在寻找相同的内容,这里有一个快速的单线:

var json = JSON.stringify(obj, function (k, v)  return k && v && typeof v !== "number" ? (Array.isArray(v) ? "[object Array]" : "" + v) : v; );

***对象没有键,所以它总是简单地返回,但是下一层不是“数字”的任何东西都将被转换为字符串,包括。数组的特殊情况,否则会暴露得更多。

如果你不喜欢这种特殊的数组情况,请使用我的旧解决方案,我也改进了:

var json = JSON.stringify(obj, function (k, v)  return k && v && typeof v !== "number" ? "" + v : v; ); // will expose arrays as strings.

在顶层传递数组而不是对象仍然适用于两种解决方案。

示例:

var obj = 
  keyA: "test",
  keyB: undefined,
  keyC: 42,
  keyD: [12, "test123", undefined]

obj.keyD.push(obj);
obj.keyE = obj;

var arr = [12, "test123", undefined];
arr.push(arr);

var f = function (k, v)  return k && v && typeof v !== "number" ? (Array.isArray(v) ? "[object Array]" : "" + v) : v; ;
var f2 = function (k, v)  return k && v && typeof v !== "number" ? "" + v : v; ;

console.log("object:", JSON.stringify(obj, f));
console.log("array:", JSON.stringify(arr, f));
console.log("");
console.log("with array string cast, so the array gets exposed:");
console.log("object:", JSON.stringify(obj, f2));
console.log("array:", JSON.stringify(arr, f2));

【讨论】:

不能正确处理数组。 @JamesWilkins 数组将被转换为字符串,使其可读,超出深度限制。我更新了我的答案以显示 [object Array] 不将数组暴露到第二级,但保留了我的旧解决方案。我还解决了数字将被转换为字符串的问题,我根本不转换未定义的,所以它们将被忽略。尽管如此,应该事先将数组作为***对象传递。 对不起,我不够清楚。我的意思是我认为具有原始值的数组与字符串具有相同的级别,它们就像字符数组(字符串)。在我的书中(可以这么说)将数组转换为字符串或“对象数组”不是正确的值。 a: [1,2,3], b: 1, c: “123” 都是一层。 在 C、C++、C# 等其他语言中,您可以更容易地看到整数数组与字符数组没有太大区别。在这两种情况下,对象都不是。也就是说,javascript 数组恰好是一个对象,但我仍然认为它不应该被视为深度有限的对象。 @JamesWilkins 那么我的示例中obj.keyD 的预期输出是什么? "keyD":[12,"test123",,[object Object]]?得出一个合乎逻辑的完美解决方案并不容易,因为尽管如此,有些情况会导致问题。就像我从第一级开始的数组中有一个数组怎么办?我相信,一个最佳的解决方案不会成为一个单一的班轮。所以这只是针对序列化而不需要反序列化,稍后。在我的情况下,我想出了这个解决方案来存储一些对象以供以后检查。【参考方案3】:

对您的对象进行深度克隆(使用诸如 low-dash 之类的库),执行您想做的任何修剪,然后将其传递给 JSON.stringify。我不会尝试重新发明 JSON.stringify,那是在错误的地方努力。

[EDIT] 看起来有人已经按照你的建议做了:JSON.stringify deep objects

我不建议这样做,因为原生 JSON.stringify 总是会更快、更健壮

[EDIT]这里是一个似乎可以做你想做的事的库:http://philogb.github.io/jit/static/v20/Docs/files/Core/Core-js.html#$jit.json.prune

【讨论】:

你的意思是有限深度的克隆 是的,可以这样做,可能比复制和修剪更有效 你的意思是lodash

以上是关于限制 JSON 字符串化深度的主要内容,如果未能解决你的问题,请参考以下文章

统计字典或者json字符串最大深度

对熊猫数据框的深度嵌套 JSON 响应

JSON字符串化一个集合

如何使用 SwiftyJSON 从深度嵌套的 JSON 字典中获取字符串 [重复]

从 JSON 中提取深度嵌套的值

Json - 字符串化,使数组在一行上