json deserialize from legacy property names
This can be done with a custom IContractResolver
created by extending DefaultContractResolver
:
[System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class LegacyDataMemberNamesAttribute : Attribute
{
public LegacyDataMemberNamesAttribute() : this(new string[0]) { }
public LegacyDataMemberNamesAttribute(params string[] names) { this.Names = names; }
public string [] Names { get; set; }
}
public class LegacyPropertyResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
for (int i = 0, n = properties.Count; i < n; i++)
{
var property = properties[i];
if (!property.Writable)
continue;
var attrs = property.AttributeProvider.GetAttributes(typeof(LegacyDataMemberNamesAttribute), true);
if (attrs == null || attrs.Count == 0)
continue;
// Little kludgy here: use MemberwiseClone to clone the JsonProperty.
var clone = property.GetType().GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var name in attrs.Cast<LegacyDataMemberNamesAttribute>().SelectMany(a => a.Names))
{
if (properties.Any(p => p.PropertyName == name))
{
Debug.WriteLine("Duplicate LegacyDataMemberNamesAttribute: " + name);
continue;
}
var newProperty = (JsonProperty)clone.Invoke(property, new object[0]);
newProperty.Readable = false;
newProperty.PropertyName = name;
properties.Add(newProperty);
}
}
return properties;
}
}
Then add attributes to your type as shown in the question:
[DataContract]
class TestObject
{
[LegacyDataMemberNames("alpha", "omega")]
[DataMember(Name = "a")]
public int A { get; set; }
}
Construct and configure an instance of LegacyPropertyResolver
, e.g. as follows:
static IContractResolver legacyResolver = new LegacyPropertyResolver
{
// Configure as required, e.g.
// NamingStrategy = new CamelCaseNamingStrategy()
};
And then use it in settings
:
var settings = new JsonSerializerSettings { ContractResolver = legacyResolver };
var deserialized = JsonConvert.DeserializeObject<TestObject>(jsonString, settings);
Notes:
This implementation doesn't require that the class have explicit data contract attribute annotation. You could add that restriction, if you prefer.
You should cache and reuse instances of contract resolvers for best performance.
Demo fiddle here.
A very simple solution using Json.NET is to just provide a legacy property with a setter only.
class TestObject {
public int A { get; set; }
public int alpha { set => A = value; }
public int omega { set => A = value; }
}
You'd probably rather not have these public, in which case you can just mark private
and add the JsonProperty
attribute.
class TestObject {
public int A { get; set; }
[JsonProperty] private int alpha { set => A = value; }
[JsonProperty] private int omega { set => A = value; }
}