问题
I am using Newtonsoft JSON.Net to deserialize an object with PreserveReferencesHandling enabled. jQuery does not support relinking references based on the $ref and $id syntax JSON.Net uses (I don't know if jQuery supports this functionality in any capacity).
I tried using Douglas Crockford's cycle.js but that does not seem to work with my objects, the returned object is identical to the object which got passed in.
I am not incredibly familiar with JSON.Net, but I cannot seem to find any javascript libraries which would serialize (or parse) the JSON their .NET component outputs.
How can I accomplish putting back together object references?
回答1:
I was looking for a solution to this problem as well, and ended up hacking Douglas Crockford's JSON.retrocycle function. His function does not work for the $ref=some number, but it looks for something like an xpath.
This is my quick and dirty version - don't use this as is - I'm not doing any cleanup, and it probably should be a plugin, but it does the job and is good enough to get going:
function retrocycle(o) {
var self = this;
self.identifiers = [];
self.refs = [];
self.rez = function (value) {
// The rez function walks recursively through the object looking for $ref
// properties. When it finds one that has a value that is a path, then it
// replaces the $ref object with a reference to the value that is found by
// the path.
var i, item, name, path;
if (value && typeof value === 'object') {
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (i = 0; i < value.length; i += 1) {
item = value[i];
if (item && typeof item === 'object') {
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[i] = self.identifiers[parseInt(path)]
} else {
self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
}
} else {
for (name in value) {
if (typeof value[name] === 'object') {
item = value[name];
if (item) {
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[name] = self.identifiers[parseInt(path)]
} else {
self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
}
}
}
}
};
self.rez(o);
self.identifiers = [];
}
Use it like this:
$.post("url/function", { ID: params.ID }, function (data) {
retrocycle(data)
// data references should be fixed up now
}, "json");
回答2:
You would have to write in a double look-up into your js parser. Technically preserving reference handling is to get around circular references, which is to say what would normally cause a stack overflow during parsing.
JSON does not have a native syntax for handling this. Newtonsoft version is a custom implementation, thus parsing the JSON will be a custom implementation.
If you really have to preserve such references, XML may be a better solution. There are some json->xml libraries out there.
Here is one solution for parsing that may be of use, or at least a guide: https://blogs.oracle.com/sundararajan/entry/a_convention_for_circular_reference
回答3:
This is my enhanced version of @Dimitri. @Dimitri code sometimes isn't able to rebuild the references. If anyone improves the code, please, tell me.
Regards, Marco Alves.
if (typeof JSON.retrocycle !== 'function') {
JSON.retrocycle = function retrocycle(o) {
//debugger;
var self = this;
self.identifiers = [];
self.refs = [];
self.buildIdentifiers = function (value) {
//debugger;
if (!value || typeof value !== 'object') {
return;
}
var item;
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (var i = 0; i < value.length; i += 1) {
item = value[i];
if (!item || !item.$id || isNaN(item.$id)) {
if (item) {
self.buildIdentifiers(item);
}
continue;
}
self.identifiers[parseInt(item.$id)] = item;
self.buildIdentifiers(item);
}
return;
}
for (var name in value) {
if (typeof value[name] !== 'object') {
continue;
}
item = value[name];
if (!item || !item.$id || isNaN(item.$id)) {
if (item) {
self.buildIdentifiers(item);
}
continue;
}
self.identifiers[parseInt(item.$id)] = item;
self.buildIdentifiers(item);
}
};
self.rez = function (value) {
// The rez function walks recursively through the object looking for $ref
// properties. When it finds one that has a value that is a path, then it
// replaces the $ref object with a reference to the value that is found by
// the path.
var i, item, name, path;
if (value && typeof value === 'object') {
if (Object.prototype.toString.apply(value) === '[object Array]') {
for (i = 0; i < value.length; i += 1) {
item = value[i];
if (item && typeof item === 'object') {
if (item.$ref)
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[i] = self.identifiers[parseInt(path)];
continue;
}
//self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
} else {
for (name in value) {
if (typeof value[name] === 'object') {
item = value[name];
if (item) {
path = item.$ref;
if (typeof path === 'string' && path != null) {
//self.refs[parseInt(path)] = {};
value[name] = self.identifiers[parseInt(path)];
continue;
}
//self.identifiers[parseInt(item.$id)] = item;
self.rez(item);
}
}
}
}
}
};
self.buildIdentifiers(o);
self.rez(o);
self.identifiers = []; // Clears the array
};
}
来源:https://stackoverflow.com/questions/12865083/is-there-a-javascript-serializer-for-json-net