1

Lets say I have some class that I have previously serialized with Json.Net

public class BillingAddress
{
    public string BillingCity { get; set; }
    public string BillingState { get; set; }
    public string BillingStreet { get; set; }
    public string BillingZip { get; set; }
}

However, I had to go back and change BillingStreet to BillingStreet1, and then again later on to BillingStreetA. I am trying to find a way in Json.Net to support either an attribute or custom converter that can deserialize a property that has been known as a different name previously, and also supports having more than one different previous name (such as in my example).

I have a custom converter that relies on an attribute as follows:

[JsonVersionedPropertyName("BillingStreetA", "BillingStreet1")]
public string BillingStreet { get; set; }



public class VersionedPropertyNameConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        //return objectType.IsClass;
        return (objectType == typeof(IPaymentMethod));
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object instance = objectType.GetConstructor(Type.EmptyTypes).Invoke(null);
        PropertyInfo[] props = objectType.GetProperties();
        var versionedProps = props.Where(x => Attribute.IsDefined(x, typeof(JsonVersionedPropertyName)));

        JObject jo = JObject.Load(reader);
        foreach (JProperty jp in jo.Properties())
        {
            foreach (var versProp in versionedProps)
            {
                var attrs = versProp.GetCustomAttributes(true);
                foreach (var att in attrs)
                {
                    var versionedProp = att as JsonVersionedPropertyName;
                    if (versionedProp != null)
                    {
                        var versions = versionedProp.VersionedNames;
                        PropertyInfo prop = props.FirstOrDefault(pi => 
                            pi.CanWrite && (string.Equals(pi.Name, jp.Name, StringComparison.OrdinalIgnoreCase) || versions.Contains(jp.Name)));
                        if (prop != null)
                            prop.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
                    }
                }
            }
        }

        return instance;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

However, when I try to use this converter with another converter, my my entire deserialization fails. I am trying to figure out if theres a better way to do this (versioned property names) instead of what I have above.

1 Answer 1

1

You're close. Something like this works. I'll leave it as an excercise to genericize it :):

public class OninaigConverter : JsonConverter
{
    private readonly Dictionary<string, string> _propertyMappings = new Dictionary<string, string>
    {
        {"BillingStreetA", "BillingStreet"},
        {"BillingStreet1", "BillingStreet"}
    };

    public override bool CanWrite => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.GetTypeInfo().IsClass;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object instance = Activator.CreateInstance(objectType);
        var props = objectType.GetTypeInfo().DeclaredProperties.ToList();

        JObject jo = JObject.Load(reader);
        foreach (JProperty jp in jo.Properties())
        {
            if (!_propertyMappings.TryGetValue(jp.Name, out var name))
                name = jp.Name;

            PropertyInfo prop = props.FirstOrDefault(pi =>
                pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name);

            prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
        }

        return instance;
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.