2

I have a bunch of custom JsonConverters where some are in charge of serialiation, and some of deserialization. I add them to a JsonSerializer using the Converters property.

My problem is that if I have one JsonConverter for serializing a specific type, and another for deserializing the same type, the correct JsonConverter is not chosen for the specific task, it is always the first one in the list of Converters, where CanConvert returns true, that is chosen, regardless of whether that JsonConverter CanWrite or not.

In the below example, both CustomConverterA and CustomConverterB CanHandle the string type, but only CustomConverterB can write (the only difference is that CanWrite returns true for CustomConverterB). I would expect CustomConverterB to be chosen.

But CustomConverterA is chosen, however its WriteJson method is never called. CustomConverterB is skipped entirely since CustomConverterA was chosen before it.

public class CustomConverterA : JsonConverter
{
    public override bool CanWrite
    {
        get
        {
            Console.WriteLine("Inside CustomConverterA.CanWrite (returns false)");
            return false;
        }
    }
    public override bool CanRead => true;

    public override bool CanConvert(Type objectType)
    {
        Console.WriteLine("Inside CustomConverterA.CanConvert");

        if (objectType == typeof(string))
        {
            Console.WriteLine("return true");
            return true;
        }

        Console.WriteLine("return false");
        return false;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Console.WriteLine("Inside CustomConverterA.WriteJson");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Console.WriteLine("Inside CustomConverterA.ReadJson");

        return new object();
    }
}

public class CustomConverterB : JsonConverter
{
    public override bool CanWrite
    {
        get
        {
            Console.WriteLine("Inside CustomConverterB.CanWrite (returns true)");
            return true;
        }
    }
    public override bool CanRead => true;

    public override bool CanConvert(Type objectType)
    {
        Console.WriteLine("Inside CustomConverterB.CanConvert");

        if (objectType == typeof(string))
        {
            Console.WriteLine("return true");
            return true;
        }

        Console.WriteLine("return false");
        return false;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Console.WriteLine("Inside CustomConverterB.WriteJson");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Console.WriteLine("Inside CustomConverterB.ReadJson");

        return new object();
    }
}

public void TestCustomConverter()
{
    var serializer = new JsonSerializer()
    {
        Converters = {
            new CustomConverterA(),
            new CustomConverterB()
        }
    };

    var data = "testdata";

    using (var writer = new StringWriter())
    {
        serializer.Serialize(writer, data);
        var serialized = writer.ToString();
        Console.WriteLine("Serialized: " + serialized);
    }
}

The above example outputs:

Inside CustomConverterA.CanConvert
return true
Inside CustomConverterA.CanWrite (returns false)
Serialized: "testdata"

Are JsonConverters supposed to work like this? Am I not supposed to be able to have different classes handle serialization and deserialization? If not, what is the purpose of the CanWrite and CanRead properties?

DukeOf1Cat
  • 1,087
  • 15
  • 34
  • it might be checking CanConvert for choosing converter https://www.newtonsoft.com/json/help/html/SerializationSettings.htm#Converters – mike m Oct 31 '17 at 15:12
  • 1
    Rather than falling back to the second converter, when `CanConvert` returns `false`, Json.NET falls back to default serialization, as explained [here](https://stackoverflow.com/a/29616648/3744182). You can confirm this in the source code for [`JsonSerializerInternalWriter.SerializeValue()`](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs#L164). – dbc Oct 31 '17 at 18:34
  • Yes, it seems like it only chooses the first JsonConverter where CanConvert returns true, regardless of whether it CanWrite or not. My opinion is that this is unexpected behaviour. I'll post an issue in the GitHub repo. – DukeOf1Cat Nov 01 '17 at 10:16

0 Answers0