Custom deserializer only for some fields with json.NET

A possible way is to create a proxy property to serialize and deserialize correctly. By using the ScriptIgnoreAttribute (also possible is the JsonIgnoreAttribute) the real property don't gets serialized. Here is an example:

[ScriptIgnore]
public int RealProperty { get; set; }

public string RealPropertyProxy
{
    get
    {
        return SerializeRealProperty(RealProperty);
    }
    set
    {
        RealProperty = DeserializeRealProperty(value);
    }
}

The result is that only the proxy gets serialized the way you defined it (based on the value of the real property). Just modify the properties needed to be serialized in a special way and you don't need to implement a special JsonConverter.


Since you are annotating your type with Json.NET attributes anyway, a simpler solution would seem to be to put the converters on the relevant properties using [JsonConverter(Type)] or [JsonProperty(ItemConverterType = Type)]:

public class Configuration
{
    public int a { get; set; }
    public int b { get; set; }
    public Obj1 obj1 { get; set; }

    // Converts the entire list to a compressed string
    [JsonConverter(typeof(IntListConverter))]
    public int[] c { get; set; }

    // Converts each Obj2 item individually
    [JsonProperty(ItemConverterType = typeof(Obj2Converter))]
    public IList<Obj2> obj2 { get; set; }
}

Nevertheless, if you need to retain the converter on Configuration (or are actually adding the converter to JsonSerializerSettings.Converters and cannot add Json.NET attributes to your type), you can use JsonSerializer.Populate() to populate the standard properties, as long as you first remove the custom properties from the JObject:

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        var jsonObject = JObject.Load(reader);

        var configuration = (existingValue as Configuration ?? new Configuration());

        // I created the JsonConverter for those 2 properties
        configuration.c = myCustomProcessMethod(jsonObject["c"].RemoveFromLowestPossibleParent());
        configuration.obj2 = myCustomProcessMethod2(jsonObject["obj2"].RemoveFromLowestPossibleParent().ToObject<ValletConfiguration>());

        // Populate the remaining standard properties
        using (var subReader = jsonObject.CreateReader())
        {
            serializer.Populate(subReader, configuration);
        }

        return configuration;
    }

Using the extension method:

public static class JsonExtensions
{
    public static JToken RemoveFromLowestPossibleParent(this JToken node)
    {
        if (node == null)
            return null;
        var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
        if (contained != null)
            contained.Remove();
        // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
        if (node.Parent is JProperty)
            ((JProperty)node.Parent).Value = null;
        return node;
    }
}