My C# program is running into StackOverflowException, when I try to serialize object with similar structure like this:
- Object has members which reference each other
- can't be try catched (idk why)
- if count is set below 6500 (may vary depending on machine) it is successfully serialized
Example code below:
class Chacha
{
public Chacha NextChacha { get; set; }
}
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
static void Main(string[] args)
{
int count = 15000;
Chacha[] steps = new Chacha[count];
steps[0] = new Chacha();
for (int i = 1; i < count; i++)
{
steps[i] = new Chacha();
steps[i-1].NextChacha = steps[i];
}
string serSteps = JsonConvert.SerializeObject(steps, Settings);
}
JSON.NET version is: 9.0.1
.NET Framework: 4.5.2
Any solutions how to serialize this structure?
Any help or suggestion is welcomed. Thank you
The reason you are getting the stackoverflow exception is that Json.NET is a recursive, single-pass tree or graph serializer that, when
PreserveReferencesHandling.Objects
is enabled, always serializes the first occurrence of each object. You have constructed your 15,000 elementChacha []
array so that the first entry is the head of a linked list containing all the other items linked sequentially. Json.NET will try to serialize that to nested JSON objects 15,000 levels deep via 15,000 levels of recursion, overflowing the stack in the process.Thus what you need to do is write the entire table of linkages only at the head of the list, as a JSON array. Unfortunately, however, Json.NET is also a contract-based serializer which means it will try to write the same properties whenever it encounters an object of a given type, no matter what the nesting depth is. Thus adding a
Chacha[] NextChachaList
property to yourChacha
object doesn't help since it will get written at each level. Instead it will be necessary to create a fairly complex customJsonConverter
that tracks the serialization depth in a thread-safe manner and only writes the linkage list only at the top level. The following does the trick:Then, given the slightly modified class
Chacha
:The following JSON is generated for an array of 3 items:
Notice that the JSON depth is now strictly limited. Example fiddle.
Be aware that, once you specify a custom converter for your type, it needs to do everything manually. If your type
Chacha
is polymorphic and you need to read and write"$type"
properties, you will need to add that logic to the converter yourself.By the way, I recommend
TypeNameHandling.Objects
instead ofTypeNameHandling.All
. Object types may reasonably be specified in the JSON (as long as the types are properly sanitized) but collection types should be specified in the code. Doing so makes it possible to switch from an array to aList<T>
without having to postread legacy JSON files.