当前位置: 首页 > 面试题库 >

JSON.stringify深层对象

莫骞仕
2023-03-14
问题内容

我需要一个从任何参数构建JSON有效字符串的函数,但:

  • 通过不两次添加对象来避免递归问题
  • 通过截断给定深度来避免调用堆栈大小问题

通常,它应该能够处理大对象,但要以截断为代价。

作为参考,此代码失败:

var json = JSON.stringify(window);

避免递归问题很简单:

var seen = [];
return JSON.stringify(o, function(_, value) {
    if (typeof value === 'object' && value !== null) {
        if (seen.indexOf(value) !== -1) return;
        else seen.push(value);
    }
    return value;
});

但是到目前为止,除了复制和更改Douglas
Crockford的代码
以跟踪深度之外,我还没有找到任何方法来避免在诸如window或any之类的非常深的对象上发生堆栈溢出event。有一个简单的解决方案吗?


问题答案:

我做了我最初担心的事情:我采用了Crockford的代码,并根据需要对其进行了修改。现在,它可以构建JSON但可以处理

  • 周期
  • 太深的物体
  • 数组太长
  • 异常(无法合法访问的访问器)

如果有人需要,我在GitHub上建立了一个GitHub存储库:JSON.prune

这是代码:

// JSON.pruned : a function to stringify any object without overflow
// example : var json = JSON.pruned({a:'e', c:[1,2,{d:{e:42, f:'deep'}}]})
// two additional optional parameters :
//   - the maximal depth (default : 6)
//   - the maximal length of arrays (default : 50)
// GitHub : https://github.com/Canop/JSON.prune
// This is based on Douglas Crockford's code ( https://github.com/douglascrockford/JSON-js/blob/master/json2.js )
(function () {
    'use strict';

    var DEFAULT_MAX_DEPTH = 6;
    var DEFAULT_ARRAY_MAX_LENGTH = 50;
    var seen; // Same variable used for all stringifications

    Date.prototype.toPrunedJSON = Date.prototype.toJSON;
    String.prototype.toPrunedJSON = String.prototype.toJSON;

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };

    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string'
                ? c
                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }

    function str(key, holder, depthDecr, arrayMaxLength) {
        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            partial,
            value = holder[key];
        if (value && typeof value === 'object' && typeof value.toPrunedJSON === 'function') {
            value = value.toPrunedJSON(key);
        }

        switch (typeof value) {
        case 'string':
            return quote(value);
        case 'number':
            return isFinite(value) ? String(value) : 'null';
        case 'boolean':
        case 'null':
            return String(value);
        case 'object':
            if (!value) {
                return 'null';
            }
            if (depthDecr<=0 || seen.indexOf(value)!==-1) {
                return '"-pruned-"';
            }
            seen.push(value);
            partial = [];
            if (Object.prototype.toString.apply(value) === '[object Array]') {
                length = Math.min(value.length, arrayMaxLength);
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value, depthDecr-1, arrayMaxLength) || 'null';
                }
                v = partial.length === 0
                    ? '[]'
                    : '[' + partial.join(',') + ']';
                return v;
            }
            for (k in value) {
                if (Object.prototype.hasOwnProperty.call(value, k)) {
                    try {
                        v = str(k, value, depthDecr-1, arrayMaxLength);
                        if (v) partial.push(quote(k) + ':' + v);
                    } catch (e) { 
                        // this try/catch due to some "Accessing selectionEnd on an input element that cannot have a selection." on Chrome
                    }
                }
            }
            v = partial.length === 0
                ? '{}'
                : '{' + partial.join(',') + '}';
            return v;
        }
    }

    JSON.pruned = function (value, depthDecr, arrayMaxLength) {
        seen = [];
        depthDecr = depthDecr || DEFAULT_MAX_DEPTH;
        arrayMaxLength = arrayMaxLength || DEFAULT_ARRAY_MAX_LENGTH;
        return str('', {'': value}, depthDecr, arrayMaxLength);
    };

}());

一个可以做什么的例子:

var json = JSON.pruned(window);

注意:
与该答案中的代码相反,GitHub存储库在需要时进行更新(文档,兼容性,在commonjs或节点中用作模块,特定的序列化等)。如果需要此修剪功能,则从存储库开始是个好主意。



 类似资料:
  • 问题内容: 我想使用构造函数制作对象数组的深层副本。 但是,由于某种原因,我上面的内容不起作用。我有运行的自动化测试,但未通过这些测试。所以这里有一个错误,我不确定是什么。 问题答案: 您已实现的是 浅表 副本。要实现 深度 复制,您必须进行更改 一些事情,分配一个 副本 的到。您如何执行此操作取决于班级。可能的替代方法是: 复制构造函数: 工厂方法: 克隆: 笔记: 上面假设复制构造函数,工厂方

  • 问题内容: 在“深度”对象层次结构中使用Builder模式的最佳实践是什么?详细地说,我探讨了将Joshua Bloch提出的Builder模式应用于我的XML绑定代码的想法(我使用的是SimpleXML,但是这个问题将适用于任何情况)。我的对象层次结构深达4个级别,具有不同程度的复杂性。我的意思是,在某些级别上,我的对象只有几个属性,而在其他级别上,我最多可以有10个属性。 因此,请考虑以下假设

  • 问题内容: 我陷入困境,我有一个这样的对象。 我不会遍历所有这些,所以我有这样的事情。 •东西 • 你好,世界 •您好溢出 •约翰·杜 •惊人的标题 • 谷歌一下 •我还是个孩子 •另一个 •他是我的兄弟 •她是我妈妈 •你永远不知道我是否要生孩子 问题是我不知道这个物体会走多深或里面有什么。因此我将无法手动进行。我在底部提供的小提琴中完成了一个基本的循环,但是我不知道如何自动循环这些循环并创建嵌

  • 问题内容: 实现深层对象复制功能有点困难。您采取什么步骤来确保原始对象和克隆对象没有引用? 问题答案: 一种安全的方法是序列化对象,然后反序列化。这样可以确保所有内容都是全新的参考。 这是有关如何有效执行此操作的文章。 注意事项:类可能会覆盖序列化,这样就不会创建新实例,例如单例。如果您的课程不是可序列化的,那么这当然也行不通。

  • 问题内容: 我有以下对象: 我想检索以下结果: abc [1] abc [2] abc [3] abc [4] abc.count abc.counter.count abc [5] abc [5] .test abc [5] .tester abc [5] .tester.name 可以在插件的帮助下使用nodejs吗? 问题答案: 您可以通过递归遍历对象来做到这一点: 在问题中的对象上运行将返

  • 问题内容: 我正在尝试开发一个脱机HTML5应用程序,该应用程序应可在大多数现代浏览器(Chrome,Firefox,IE 9 +,Safari,Opera)中使用。由于Safari目前尚未支持IndexedDB,并且不建议使用WebSQL,因此我决定使用localStorage来存储用户生成的JavaScript对象和/ 放入或取出对象。但是,我发现这不能处理方法。这是带有简单方法的示例对象: