Well this was fun...
When I looked more closely at the stack trace for the exception, I noticed that the method JsonSerializerInternalWriter.SerializeConvertable
was in there twice, indeed it was that method one off the top of the stack - invoking JsonSerializerInternalWriter.CheckForCircularReference
- which in turn was throwing the exception. It was also, however, the source of the call to my own converter's Write
method.
So it would seem that the serializer was doing:
- 1) If object has a converter
- 1a) Throw if circular reference
- 1b) Invoke converter's Write method
- 2) Else
- 2a) Use internal serializers
So, in this case, the Json.Net is calling my converter which in turn is calling the Json.Net serializer which then blows up because it sees it's already serializing the object that was passed to it!
Opening ILSpy on the DLL (yes I know it's open source - but I want the 'callers' functionality!) and moving up the call stack from SerializeConvertable
to JsonSerializerInternalWriter.SerializeValue
, the code that detects whether a converter should be used can be found right near the start:
if (((jsonConverter = ((member != null) ? member.Converter : null)) != null
|| (jsonConverter = ((containerProperty != null) ? containerProperty.ItemConverter
: null)) != null
|| (jsonConverter = ((containerContract != null) ? containerContract.ItemConverter
: null)) != null
|| (jsonConverter = valueContract.Converter) != null
|| (jsonConverter =
this.Serializer.GetMatchingConverter(valueContract.UnderlyingType)) != null
|| (jsonConverter = valueContract.InternalConverter) != null)
&& jsonConverter.CanWrite)
{
this.SerializeConvertable(writer, jsonConverter, value, valueContract,
containerContract, containerProperty);
return;
}
Thankfully that very last condition in the if
statement provides the solution to my issue: all I had to do was to add the following to either the base converter copied from the code in the linked SO in the question, or in the derived one:
public override bool CanWrite
{
get
{
return false;
}
}
And now it all works fine.
The upshot of this, however, is that if you intend to have some custom JSON serialization on an object and you are injecting it with a converter and you intend to fallback to the standard serialization mechanism under some or all situations; then you can't because you will fool the framework into thinking you're trying to store a circular reference.
I did try manipulating the ReferenceLoopHandling
member, but if I told it to Ignore
them then nothing was serialized and if I told it to save them, unsurprisingly, I got a stack overflow.
It's possible that this is a bug in Json.Net - alright it's so much of an edge-case that it's in danger of falling off the edge of the universe - but if you do find yourself in this situation then you're kind of stuck!