Reputation: 477
I have a data model made of a TransformationPlanModel
which in turn contains an IEnumerable<TransformationModel>
. TransformationPlanModel
is the root data transfer object. TransformationModel
is an abstract base class for other transformation models.
Snipped code for TransformationPlanModel
:
public class TransformationPlanModel
{
// snip
public IEnumerable<TransformationModel> Transformations { get; set; }
}
Snipped code for TransformationModel
:
public abstract class TransformationModel
{
//snip
public string Key { get; set; }
}
One of concrete transformation models is RemoveRowsTransformationModel
:
public class RemoveRowsTransformationModel : TransformationModel
{
public const string KeyConst = "removeRows";
public int TopRowsCount { get; set; }
public int BottomRowsCount { get; set; }
public RemoveRowsTransformationModel()
{
Key = KeyConst;
}
//snip
}
I've written a custom JsonConverter
which converts the TransformationModel
inheritance tree. The implementation of JsonWrite
method is:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject.FromObject(value, serializer).WriteTo(writer);
}
The custom converter is used in an ASP.NET Core API application and is added to the services in the Startup.ConfigureServices
method like so:
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new TransformationConverter());
});
When a TransformationPlanModel
is returned as a response from an API endpoint and is serialized, a Newtonsoft.Json.JsonSerializationException
is thrown containing the message: "Self referencing loop detected with type '(...).RemoveRowsTransformationModel'". What causes this exception? I fail to see where the self referencing loop is. I don't know if it matters, but I'm using .NET Core 2.0 Preview 1.
Interestingly enough, when an overload of JObject.FromObject
without the serializer parameter is used in the WriteJson
method, it works properly:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject.FromObject(value).WriteTo(writer);
}
This however resets the serialization settings, like camel case property naming. I've also tried ignoring the circular references in Json.Net settings, but no transformations are serialized then.
Upvotes: 2
Views: 703
Reputation: 129707
The "self-referencing loop" the error is referring to in this case is your converter calling itself. The serializer has settings which direct it to use your converter. Your converter calls JObject.FromObject()
, passing in the serializer. JObject.FromObject
uses the serializer to to try to create a JObject
from your model. The serializer then calls your converter again, and so on. Json.Net detects this recursive loop and throws an exception.
One possible solution is to use a new serializer instance, copying all the settings from the original serializer except for the converter:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JsonSerializer ser2 = new JsonSerializer();
foreach (PropertyInfo prop in typeof(JsonSerializer).GetProperties()
.Where(p => p.Name != nameof(JsonSerializer.Converters)))
{
prop.SetValue(ser2, prop.GetValue(serializer));
}
JObject.FromObject(value, ser2).WriteTo(writer);
}
However, since your WriteJson
method doesn't appear to do anything other than just loading the object and immediately writing it out without any changes, a better solution is simply to override CanWrite
to return false. Then WriteJson
will not be called at all and the default serialization will be used instead, which seems to be what you want to happen here anyway.
public override bool CanWrite
{
get { return false; }
}
Upvotes: 4