如何从Json.NET序列化的JSON中恢复循环引用(例如“$ id”)?

时间:2014-02-10 19:46:24

标签: javascript json serialization circular-reference

是否有现有的javascript库可以通过引用循环处理对Json.Net进行反序列化?

{
    "$id": "1",
    "AppViewColumns": [
        {
            "$id": "2",
            "AppView": {"$ref":"1"},
            "ColumnID": 1,
        }
    ]
}

这应该反序列化为一个对象,在数组中的对象和外部对象之间有一个引用循环

3 个答案:

答案 0 :(得分:3)

给出的答案几乎对我有用,但最新版本的MVC,JSON.Net和DNX使用“$ ref”和“$ id”,它们可能会出现故障。所以我修改了user2864740的答案。

我应该注意,这段代码不会处理数组引用,这也是可能的。

function RestoreJsonNetReferences(g) {
	var ids = {};

	function getIds(s) {
		// we care naught about primitives
		if (s === null || typeof s !== "object") { return s; }

		var id = s['$id'];
		if (typeof id != "undefined") {
			delete s['$id'];

			// either return previously known object, or
			// remember this object linking for later
			if (ids[id]) {
				throw "Duplicate ID " + id + "found.";
			}
			ids[id] = s;
		}

		// then, recursively for each key/index, relink the sub-graph
		if (s.hasOwnProperty('length')) {
			// array or array-like; a different guard may be more appropriate
			for (var i = 0; i < s.length; i++) {
				getIds(s[i]);
			}
		} else {
			// other objects
			for (var p in s) {
				if (s.hasOwnProperty(p)) {
					getIds(s[p]);
				}
			}
		}
	}

	function relink(s) {
		// we care naught about primitives
		if (s === null || typeof s !== "object") { return s; }

		var id = s['$ref'];
		delete s['$ref'];

		// either return previously known object, or
		// remember this object linking for later
		if (typeof id != "undefined") {
			return ids[id];
		}

		// then, recursively for each key/index, relink the sub-graph
		if (s.hasOwnProperty('length')) {
			// array or array-like; a different guard may be more appropriate
			for (var i = 0; i < s.length; i++) {
				s[i] = relink(s[i]);
			}
		} else {
			// other objects
			for (var p in s) {
				if (s.hasOwnProperty(p)) {
					s[p] = relink(s[p]);
				}
			}
		}

		return s;
	}

	getIds(g);
	return relink(g);
}

答案 1 :(得分:1)

好的,所以我创建了一个更健壮的方法,它将使用$ id和$ ref,因为这实际上是json.net处理循环引用的方式。此外,您必须在ID已注册后获取您的引用,否则它将找不到已引用的对象,因此我还必须保留请求引用的对象,以及它们要设置的属性和id他们正在请求。

这是基于lodash / underscore的

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        define(['lodash'], factory);
    } else {
        factory(_);
    }
})(function (_) {
    var opts = {
        refProp: '$ref',
        idProp: '$id',
        clone: true
    };
    _.mixin({
        relink: function (obj, optsParam) {
            var options = optsParam !== undefined ? optsParam : {};
            _.defaults(options, _.relink.prototype.opts);
            obj = options.clone ? _.clone(obj, true) : obj;
            var ids = {};
            var refs = [];
            function rl(s) {
                // we care naught about primitives
                if (!_.isObject(s)) {
                    return s;
                }
                if (s[options.refProp]) {
                    return null;
                }
                if (s[options.idProp] === 0 || s[options.idProp]) {
                    ids[s[options.idProp]] = s;
                }
                delete s[options.idProp];
                _(s).pairs().each(function (pair) {
                    if (pair[1]) {
                        s[pair[0]] = rl(pair[1]);
                        if (s[pair[0]] === null) {
                            if (pair[1][options.refProp] !== undefined) {
                                refs.push({ 'parent': s, 'prop': pair[0], 'ref': pair[1][options.refProp] });
                            }
                        }
                    }
                });

                return s;
            }

            var partialLink = rl(obj);
            _(refs).each(function (recordedRef) {
                recordedRef['parent'][recordedRef['prop']] = ids[recordedRef['ref']] || {};
            });
            return partialLink;
        },
        resolve: function (obj, optsParam) {
            var options = optsParam !== undefined ? optsParam : {};
            _.defaults(options, _.resolve.prototype.opts);
            obj = options.clone ? _.clone(obj, true) : obj;
            var objs = [{}];

            function rs(s) {
                // we care naught about primitives
                if (!_.isObject(s)) {
                    return s;
                }
                var replacementObj = {};

                if (objs.indexOf(s) != -1) {
                    replacementObj[options.refProp] = objs.indexOf(s);
                    return replacementObj;
                }
                objs.push(s);
                s[options.idProp] = objs.indexOf(s);
                _(s).pairs().each(function (pair) {
                    s[pair[0]] = rs(pair[1]);
                });

                return s;
            }

            return rs(obj);
        }
    });
    _(_.resolve.prototype).assign({ opts: opts });
    _(_.relink.prototype).assign({ opts: opts });
});

我创建了gist here

答案 2 :(得分:0)

我不知道现有的库有这样的支持,但是一个可以使用标准的JSON.parse方法,然后手动遍历恢复循环引用的结果 - 它只是基于$ id属性的简单存储/查找。 (类似的方法可用于逆转过程。)

以下是一些使用此类方法的示例代码。此代码假定JSON已经解析为相关的JS对象图 - 它还修改了提供的数据。 YMMV。

function restoreJsonNetCR(g) {
  var ids = {};

  function relink (s) {
    // we care naught about primitives
    if (s === null || typeof s !== "object") { return s; }

    var id = s['$id'];
    delete s['$id'];

    // either return previously known object, or
    // remember this object linking for later
    if (ids[id]) {
      return ids[id];
    }
    ids[id] = s;

    // then, recursively for each key/index, relink the sub-graph
    if (s.hasOwnProperty('length')) {
      // array or array-like; a different guard may be more appropriate
      for (var i = 0; i < s.length; i++) {
        s[i] = relink(s[i]);
      }
    } else {
      // other objects
      for (var p in s) {
        if (s.hasOwnProperty(p)) {
          s[p] = relink(s[p]);
        }
      }
    }

    return s;
  }

  return relink(g);
}

用法

var d = {
    "$id": "1",
    "AppViewColumns": [
        {
            "$id": "2",
            "AppView": {"$id":"1"},
            "ColumnID": 1,
        }
    ]
};

d = restoreJsonNetCR(d);
// the following works well in Chrome, YMMV in other developer tools
console.log(d);

DrSammyD创建了一个下划线插件变体with round-trip support