Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Json.NET custom serialization with JsonConverter - how to get the "default" behavior

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)

like image 815
j_maly Avatar asked Feb 23 '16 19:02

j_maly


People also ask

How does the Jsonproperty attribute affect JSON serialization?

JsonPropertyAttribute indicates that a property should be serialized when member serialization is set to opt-in. It includes non-public properties in serialization and deserialization. It can be used to customize type name, reference, null, and default value handling for the property value.

Which of the following attributes are required to ignore properties for JSON serializer?

To ignore individual properties, use the [JsonIgnore] attribute.

What is Jsonconvert DeserializeObject?

DeserializeObject<T>(String,JsonConverter[]) Deserializes the JSON to the specified . NET type using a collection of JsonConverter. DeserializeObject(String, JsonSerializerSettings) Deserializes the JSON to a .


1 Answers

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:

  • This does not handle situations when TypeNameHandling has been enabled and a "$type" property is present specifying a polymorphic subtype.

    In this case you'll need to do some of the tricks use by JsonDerivedTypeConverer<T> in JsonConverter with Interface.

  • The type to be deserialized must have a parameterless constructor accessible to Json.NET. If not, and existingValue is null, it will be necessary to construct it manually, via new DataType(arg1, arg2, ...).

  • Reference preservation via PreserveReferencesHandling is not supported.

    For one approach to handle this situation see How can I choose what type to deserialize at runtime based on the structure of the json?.

Sample fiddle.

like image 52
dbc Avatar answered Sep 18 '22 16:09

dbc