【问题标题】:avoiding TypeError: Converting circular structure to JSON避免 TypeError:将循环结构转换为 JSON
【发布时间】:2014-07-27 08:07:59
【问题描述】:

我知道这已经被问过几次了,但我仍然无法找到解决这个问题的方法。 (我两周前才开始使用 KO。)

我有一堆字段,每个字段都绑定到我的 VM 中的 observables。当用户单击 GO 按钮时,我想将输入的所有字段转换为 JSON 字符串并发送到服务器。这是 ViewModel:

function ViewModel() {
    var self = this;

    function searchVar() {
        self.users = ko.observable();
        self.fromdate = ko.observable();
        self.todate = ko.observable();
        self.eqpname = ko.observable();
        self.maker = ko.observable();
        self.model = ko.observable();
        self.makercode = ko.observable();
        self.partname = ko.observable();
        self.vendor = ko.observable();
    }

    self.searchVars = ko.observableArray();

    self.addSearchVar = function (vm) {
        self.searchVars.removeAll();
        self.searchVars.push(vm);
        //console.log(vm);
        var data = ko.toJSON(self.searchVars);
        alert(data);
    };

    var sv = new searchVar(); //not really using this

}
    var vm = new ViewModel();

这里的问题是 addSearchVar 函数再次创建了对 searchVars 数组的引用,这就是导致循环引用错误的原因(我说得对吗?)。有一个更好的方法吗? (曾尝试使用嵌套模型,但无法完全理解它)。

标记非常标准。 data-bind:"value: [fieldname]" 等等。我将ko.applyBindings(vm) 放在页面底部,因为我有很多动态创建的组件。按钮属性为:<input type="button" id="btnGo" value="Go" style="background-color:#CCFFCC" data-bind="click: addSearchVar" />

欣赏任何见解。谢谢。 /aj

【问题讨论】:

    标签: json knockout.js


    【解决方案1】:

    您可以通过在对视图模型进行 json 编码之前删除/替换循环引用来防止这种情况发生。一种方法是使用 Douglas 的JSON.decycle

    if (typeof JSON.decycle !== 'function') {
        JSON.decycle = function decycle(object) {
            'use strict';
    
    // Make a deep copy of an object or array, assuring that there is at most
    // one instance of each object or array in the resulting structure. The
    // duplicate references (which might be forming cycles) are replaced with
    // an object of the form
    //      {$ref: PATH}
    // where the PATH is a JSONPath string that locates the first occurance.
    // So,
    //      var a = [];
    //      a[0] = a;
    //      return JSON.stringify(JSON.decycle(a));
    // produces the string '[{"$ref":"$"}]'.
    
    // JSONPath is used to locate the unique object. $ indicates the top level of
    // the object or array. [NUMBER] or [STRING] indicates a child member or
    // property.
    
            var objects = [],   // Keep a reference to each unique object or array
                paths = [];     // Keep the path to each unique object or array
    
            return (function derez(value, path) {
    
    // The derez recurses through the object, producing the deep copy.
    
                var i,          // The loop counter
                    name,       // Property name
                    nu;         // The new object or array
    
    // typeof null === 'object', so go on if this value is really an object but not
    // one of the weird builtin objects.
    
                if (typeof value === 'object' && value !== null &&
                        !(value instanceof Boolean) &&
                        !(value instanceof Date)    &&
                        !(value instanceof Number)  &&
                        !(value instanceof RegExp)  &&
                        !(value instanceof String)) {
    
    // If the value is an object or array, look to see if we have already
    // encountered it. If so, return a $ref/path object. This is a hard way,
    // linear search that will get slower as the number of unique objects grows.
    
                    for (i = 0; i < objects.length; i += 1) {
                        if (objects[i] === value) {
                            return {$ref: paths[i]};
                        }
                    }
    
    // Otherwise, accumulate the unique value and its path.
    
                    objects.push(value);
                    paths.push(path);
    
    // If it is an array, replicate the array.
    
                    if (Object.prototype.toString.apply(value) === '[object Array]') {
                        nu = [];
                        for (i = 0; i < value.length; i += 1) {
                            nu[i] = derez(value[i], path + '[' + i + ']');
                        }
                    } else {
    
    // If it is an object, replicate the object.
    
                        nu = {};
                        for (name in value) {
                            if (Object.prototype.hasOwnProperty.call(value, name)) {
                                nu[name] = derez(value[name],
                                    path + '[' + JSON.stringify(name) + ']');
                            }
                        }
                    }
                    return nu;
                }
                return value;
            }(object, '$'));
        };
    }
    

    https://github.com/douglascrockford/JSON-js/blob/master/cycle.js

    这会将所有循环引用替换为引用值。

    在你的情况下,你会这样使用它:

    var data = JSON.stringify(JSON.decycle(ko.toJS(self.searchVars)));
    

    另一种选择是删除所有循环引用:

    JSON.stringifyOnce = function (obj, replacer, space) {
        var cache = [];
        var json = JSON.stringify(obj, function(key, value) {
            if (typeof value === 'object' && value !== null) {
                if (cache.indexOf(value) !== -1) {
                    // circular reference found, discard key
                    return;
                }
                // store value in our collection
                cache.push(value);
            }
            return replacer ? replacer(key, value) : value;
        }, space);
        cache = null;
        return json;
    };
    

    然后:

    var data = JSON.stringifyOnce(ko.toJS(self.searchVars));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-05
      • 2016-01-22
      • 2017-06-29
      • 2019-01-05
      • 1970-01-01
      • 1970-01-01
      • 2023-01-31
      • 2020-08-18
      相关资源
      最近更新 更多