I'm having issues serializing my code first entities to JSON. I swapped out the default JsonFormatter for a JsonNetFormatter, but I still get the error "Self referencing loop detected for type 'MyParentType'".
The weird thing is that this works perfectly without code first. If I "new up" an MVC 4 project and throw in an Entity Model I don't get the above issue. However, I did notice that that the serialized JSON does look different,
and that is probably the key. The good JSON looks like this:
[{"$id":"1","MyParentTypeID":7..........
and the bad JSON:
[{"MyParentTypeID":48,............
So you can see that the good JSON has an additional property that actually gets referenced later in the JSON doc like so:
{"$ref":"1"}
Is there any way for me to get the JSON Formatter or EF Code First to allow for those references to make the serialization work?
I guess jQuery doesn't support JSON references. I just tried to load one of the JSON strings through $.parseJSON and the references were not reconstructed. You just end up with "dead ends" where the references were, an object with only a $ref property,
nothing else.
Yea, that's my concern. If you start to get too clever, then you end up where we were back in 2000 with the SOAP v1 section 5 encoding rules. Basically, it's not just XML (or JSON) anymore and you then need the same specalized framework on both ends.
skippyfire
BTW, I attended your jQuery Mobile / MVC 4 talk at the NE Code Camp 2 weeks ago. Well done!
I would think that something as simple as this would be easily supported, but I don't know if there would be other long term ramifications. But I do like this idea, and the fact that it would potentially reduce the amount of data that gets sent over the
wire.
Have you dealt with something like this in the past? The common theme behind people's responses to the circular reference issue seems to be "remove one of the paths that creates the circular reference".
For example, with a Parent and Child class both referencing each other, you would remove the "Parent" property from the Child class. But then you're left with a Child with no direct relation to the parent. Not a big deal, but you would need to get the
parent from the Parent collection given their ID, instead of just accessing a property.
I would be interested to know what your thoughts are.
I would think that something as simple as this would be easily supported, but I don't know if there would be other long term ramifications. But I do like this idea, and the fact that it would potentially reduce the amount of data that gets sent over the
wire.
Yea, I dunno why it's not included. Looks like Crockford himself
already has addressed this (which looks like it's compatible with JSON.net's format).
skippyfire
Have you dealt with something like this in the past? The common theme behind people's responses to the circular reference issue seems to be "remove one of the paths that creates the circular reference".
For example, with a Parent and Child class both referencing each other, you would remove the "Parent" property from the Child class. But then you're left with a Child with no direct relation to the parent. Not a big deal, but you would need to get the
parent from the Parent collection given their ID, instead of just accessing a property.
Well normally I don't directly expose my EF models as a JSON endpoint. But I understand wanting to have a graph from the server transmitted to the client. I suppose if the data is that complicated then perhaps the server and client can agree on these additional
serialization semantics.
Unfortunately, I think that Crockford's library uses the other way of handling references, where an xpath-like syntax is used. JSON.Net just uses an incremented counter. I'm sure it would be simple to port Crockford's code to use the reference counter
method if the need arises.
Code to "deserialize" JSON references by $id and $ref properties:
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++)
if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
return obj;
}
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj);
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i = 0; i < refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[ref[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
skippyfire
Member
34 Points
31 Posts
Code First References Serialization Issue
Apr 10, 2012 01:17 PM|LINK
Hi everyone,
I'm having issues serializing my code first entities to JSON. I swapped out the default JsonFormatter for a JsonNetFormatter, but I still get the error "Self referencing loop detected for type 'MyParentType'".
The weird thing is that this works perfectly without code first. If I "new up" an MVC 4 project and throw in an Entity Model I don't get the above issue. However, I did notice that that the serialized JSON does look different, and that is probably the key. The good JSON looks like this:
[{"$id":"1","MyParentTypeID":7..........and the bad JSON:
[{"MyParentTypeID":48,............So you can see that the good JSON has an additional property that actually gets referenced later in the JSON doc like so:
{"$ref":"1"}Is there any way for me to get the JSON Formatter or EF Code First to allow for those references to make the serialization work?
Thanks,
John
skippyfire
Member
34 Points
31 Posts
Re: Code First References Serialization Issue
Apr 10, 2012 04:07 PM|LINK
So I finally found this page in the docs:
http://james.newtonking.com/projects/json/help/ (under Serializing and Deserializing JSON -> Serialization and Preserving Object References)
Which explains to set this setting:
PreserveReferencesHandling = PreserveReferencesHandling.ObjectsIn order to... you guessed it, preserve references!
skippyfire
Member
34 Points
31 Posts
Re: Code First References Serialization Issue
Apr 10, 2012 07:08 PM|LINK
... and I wrote a blog post on the subject: http://www.johnnycode.com/blog/2012/04/10/serializing-circular-references-with-json-net-and-entity-framework/
BrockAllen
All-Star
27530 Points
4905 Posts
MVP
Re: Code First References Serialization Issue
Apr 10, 2012 07:23 PM|LINK
And how does a JavaScript client (or more specifically the browser's JSON.parse implementation) know to reconstruct the graph?
DevelopMentor | http://www.develop.com
thinktecture | http://www.thinktecture.com/
skippyfire
Member
34 Points
31 Posts
Re: Code First References Serialization Issue
Apr 10, 2012 08:17 PM|LINK
Good point. The client may not know how to reconstruct the graph.
I'm going server to server, and JSON.Net parses (it's own) JSON fine. Not sure about other server side libraries.
As far as client-side libraries go, it looks like Dojo supports references as of v1.2: http://www.sitepen.com/blog/2008/06/17/json-referencing-in-dojo/
I guess jQuery doesn't support JSON references. I just tried to load one of the JSON strings through $.parseJSON and the references were not reconstructed. You just end up with "dead ends" where the references were, an object with only a $ref property, nothing else.
According to Wikipedia this isn't standard JSON behaviour: http://en.wikipedia.org/wiki/JSON#Object_references
BTW, I attended your jQuery Mobile / MVC 4 talk at the NE Code Camp 2 weeks ago. Well done!
BrockAllen
All-Star
27530 Points
4905 Posts
MVP
Re: Code First References Serialization Issue
Apr 10, 2012 09:03 PM|LINK
Yea, that's my concern. If you start to get too clever, then you end up where we were back in 2000 with the SOAP v1 section 5 encoding rules. Basically, it's not just XML (or JSON) anymore and you then need the same specalized framework on both ends.
Cool, thanks!
DevelopMentor | http://www.develop.com
thinktecture | http://www.thinktecture.com/
skippyfire
Member
34 Points
31 Posts
Re: Code First References Serialization Issue
Apr 10, 2012 09:21 PM|LINK
Another good point.
I would think that something as simple as this would be easily supported, but I don't know if there would be other long term ramifications. But I do like this idea, and the fact that it would potentially reduce the amount of data that gets sent over the wire.
Have you dealt with something like this in the past? The common theme behind people's responses to the circular reference issue seems to be "remove one of the paths that creates the circular reference".
For example, with a Parent and Child class both referencing each other, you would remove the "Parent" property from the Child class. But then you're left with a Child with no direct relation to the parent. Not a big deal, but you would need to get the parent from the Parent collection given their ID, instead of just accessing a property.
I would be interested to know what your thoughts are.
BrockAllen
All-Star
27530 Points
4905 Posts
MVP
Re: Code First References Serialization Issue
Apr 11, 2012 12:36 AM|LINK
Yea, I dunno why it's not included. Looks like Crockford himself already has addressed this (which looks like it's compatible with JSON.net's format).
Well normally I don't directly expose my EF models as a JSON endpoint. But I understand wanting to have a graph from the server transmitted to the client. I suppose if the data is that complicated then perhaps the server and client can agree on these additional serialization semantics.
DevelopMentor | http://www.develop.com
thinktecture | http://www.thinktecture.com/
skippyfire
Member
34 Points
31 Posts
Re: Code First References Serialization Issue
Apr 11, 2012 01:18 PM|LINK
Nice find, and thanks for the info.
Unfortunately, I think that Crockford's library uses the other way of handling references, where an xpath-like syntax is used. JSON.Net just uses an incremented counter. I'm sure it would be simple to port Crockford's code to use the reference counter method if the need arises.
FraGMenTaToR
Member
2 Points
1 Post
Re: Code First References Serialization Issue
Apr 02, 2013 06:34 AM|LINK
Code to "deserialize" JSON references by $id and $ref properties:
function resolveReferences(json) { if (typeof json === 'string') json = JSON.parse(json); var byid = {}, // all objects by id refs = []; // references to objects that could not be resolved json = (function recurse(obj, prop, parent) { if (typeof obj !== 'object' || !obj) // a primitive value return obj; if (Object.prototype.toString.call(obj) === '[object Array]') { for (var i = 0; i < obj.length; i++) if ("$ref" in obj[i]) obj[i] = recurse(obj[i], i, obj); else obj[i] = recurse(obj[i], prop, obj); return obj; } if ("$ref" in obj) { // a reference var ref = obj.$ref; if (ref in byid) return byid[ref]; // else we have to make it lazy: refs.push([parent, prop, ref]); return; } else if ("$id" in obj) { var id = obj.$id; delete obj.$id; if ("$values" in obj) // an array obj = obj.$values.map(recurse); else // a plain object for (var prop in obj) obj[prop] = recurse(obj[prop], prop, obj); byid[id] = obj; } return obj; })(json); // run it! for (var i = 0; i < refs.length; i++) { // resolve previously unknown references var ref = refs[i]; ref[0][ref[1]] = byid[ref[2]]; // Notice that this throws if you put in a reference at top-level } return json; }