If you work with a .NET backend that uses JSON.NET for serialization, you might encounter such weird things in the JSON result returned from a controller:
But what you actually want is the real object present there:
After reading this article, you will know how to achieve that ?
JSON.NET circular references are the issue
Let’s take a look at our problematic JSON response object once again:
$ref: "1"
is basically a reference to another object, which has already been serialized. In that case, the BestFriend
field references the first element of the result array with $id: "1"
. It turns out that these are JSON.NET circular references. However, you don’t want these references in your client-side JS code – you need the actual objects to work with.
I struggled with this issue some time ago in my JavaScript code. In the beginning, I had no idea it might come from the fact that JSON.NET is used for serialization (thanks to our colleague Rafał for pointing that out to me ?). Let’s see what exactly happens here and why.
JSON.NET serialization
The JS example presented above shows a JSON result received from the following ASP.NET controller method:
This is the full content of json
string after serialization:
Of course, this is great that JSON.NET does that. Why would we repeat the same objects multiple times in JSONs? The optimization here is that the same objects are serialized once and referenced from all places that use them.
However, if such JSONs are sent to the browser, fields with refs are of no use. We need the actual objects to be there, so we can process the results correctly using JavaScript.
Solution 1: fix the issue by reconfiguring JSON.NET serializer
The first obvious solution could be to configure JSON.NET to not use its optimization for references. Quickly searching through its documentation, we find that PreserveReferencesHandling setting is responsible for that.
If we change our controller’s code and serialize objects with PreserveReferencesHandling
set to None
:
Our JSON file will look exactly as we’d expect:
However, why would we disable JSON.NET circular references? This optimization can sometimes save us huge amounts of data to be sent over the wire. If you have thousands of objects, it might not make sense in sending them all to the client-side code.
You may also have no control of the backend/API you receive the data from. It might as well be dangerous to change the JSON serialization settings, especially if you work on a big project.
If reconfiguration of JSON.NET circular references serialization is not possible in your case, see the other solution (which we also use).
Solution 2: resolve circular references using JavaScript
So we are back to this state in JavaScript:
What we want is to have the full objects’ contents instead of $ref
elements in our JSON result.
In fact, all the data we need is already there. The first element of the array with $id: "1"
is object that should be put in place of all $ref: "1"
references.
As you can imagine, this requires a mechanism that would traverse the results tree and replace all these values according to IDs of the referenced objects.
Fortunately, we don’t need to implement it ourselves. There are few implementations of such a tool, circulating the web.
We use something called JsonNetDecycle
which we found few years back already. I noticed there’s a new version of it ported to TypeScript: jsonNetDecycle.ts. It doesn’t provide full type support as it is a few years old already, and I didn’t try it. But you can if you use TypeScript ? There’s also a cycle.js
solution available here and recommended by many folks. I believe it may also solve our issue, but again – this is not what we are using.
The version of the JsonNetDecycle
we use is this one. Simply create a new file called JsonNetDecycle.js
in your project and copy this Gist’s contents to this file. You can skip the last part starting with (function(console)
(line 112) – we won’t need it.
Now, we can use the exported JsonNetDecycle.retrocycle()
function to „fix” our JSON:
Let’s see whether this works.
This is what we get from the API before resolving JSON.NET circular references:
So we still have references there instead of real objects.
Now let’s see what happens with the result
object after executing JsonNetDecycle.retrocycle()
on it:
Wohhooa! That’s what we wanted ? There are no circular references in our JSON response anymore!