17

I have a JsonConverter for my class DataType. I would like to do some special handling when plain string used in Json as the value of a property of type DataType. In the case where the value is a "full" object, I would like to do the "normal" deserialization.

Here is my attempt

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (reader.Value != null && reader.ValueType == typeof (string))
    {
        return someSpecialDataTypeInstance;
    }
    else if (reader.TokenType == JsonToken.StartObject)
    {
        DataType dataType = serializer.Deserialize<DataType>(reader);
        return dataType;
    }
    else
    {
        throw new JsonSerializationException();
    }
}

But this doesn't work, because this line: DataType dataType = serializer.Deserialize(reader); causes infinite recursion.

Could this be done somehow easily? (without the need to manually go property-by-property)

j_maly
  • 1,091
  • 1
  • 13
  • 27
  • You skip some steps when you`re trying to deserialize . Take a look at here http://stackoverflow.com/questions/8030538/how-to-implement-custom-jsonconverter-in-json-net-to-deserialize-a-list-of-base – Andreea Dumitru Feb 23 '16 at 20:52

1 Answers1

15

One easy way to do it is to allocate an instance of your class then use JsonSerializer.Populate(JsonReader, Object). This is the way it is done in the standard CustomCreationConverter<T>:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (reader.Value != null && reader.ValueType == typeof(string))
    {
        return someSpecialDataTypeInstance;
    }
    else if (reader.TokenType == JsonToken.StartObject)
    {
        existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        serializer.Populate(reader, existingValue);
        return existingValue;
    }
    else if (reader.TokenType == JsonToken.Null)
    {
        return null;
    }
    else
    {
        throw new JsonSerializationException();
    }
}

Limitations:

Sample fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • The DefaultConvert() call throws NRE at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue). I replaced it with direct constructor call.... apart from that, thanks for your answer! – j_maly Feb 24 '16 at 07:21
  • @j_maly - i'm having the same issue, but I can't figure out what the solution is based on your comment. Can you post the modifications you made to this answer? – Suraj Dec 01 '16 at 02:21
  • @dbc - Thank you for posting that fiddle. It seems you are still calling DefaultCreator(). Like j_maly, I'm getting a NullReferenceException on DefaultCreator(). j_maly said "I replaced it with direct constructor call", but I couldn't interpret what he/she meant by that. I was looking for the replacement/fix to solve the NullReferenceException issue. – Suraj Dec 01 '16 at 12:19
  • @SFun28 - does your type have a public parameterless constructor? If not that might be the problem. If your type only has a single constructor, and that constructor is parameterized, then `DefaultCreator()` won't work. Instead you'll have to construct your type manually by calling `new MyType(arg1, arg2, ... )`. – dbc Dec 01 '16 at 17:54
  • @dbc - my type does not have a public parameterless constructor. I do have a customized contract resolver. I also know that if my JsonConverter return false for CanConvert, that json.net is able to deserialize the type using the customized contract resolver. Does that open up any options? – Suraj Dec 01 '16 at 18:12
  • @SFun28 - Something should be possible, but your situation sounds different enough from the one in this question that I'd need to see a [mcve]. Maybe you could ask another question? – dbc Dec 01 '16 at 18:23
  • @dbc - agreed. I did post a separate question here: http://stackoverflow.com/questions/40913277/how-to-get-readjson-to-return-default-behavior-as-if-canconvert-returned-fal but I should probably include some real code in there, just need to take the time to trim-down my implementation to the essentials needed to debug the issue. – Suraj Dec 01 '16 at 18:44
  • @SFun28 - Just checked your new question, I agree a full [mcve] with real would make an answer more likely. – dbc Dec 01 '16 at 19:02