json从遗留属性名称反序列化

如何设置Newtonsoft.Json以使用旧成员名称反序列化对象,但使用当前成员名称对其进行序列化?

这是一个需要序列化和反序列化的示例对象。 我给了一个属性属性,其中包含过去可能已经序列化的名称列表。

[DataContract] class TestObject { [LegacyDataMemberNames("alpha", "omega")] [DataMember(Name = "a")] public int A { get; set; } } 

我想json序列化始终使用名称“a”,但能够从任何遗留名称反序列化为一个属性,包括“alpha”和“omega”以及当前名称“a”

这可以通过扩展一个预先存在的解析器创建的自定义IContractResolver来完成,例如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 { // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." static LegacyPropertyResolver instance; static LegacyPropertyResolver() { instance = new LegacyPropertyResolver(); } public static LegacyPropertyResolver Instance { get { return instance; } } protected LegacyPropertyResolver() : base() { } protected override IList 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().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; } } 

请注意,此实现不要求类具有显式数据协定属性注释。 如果您愿意,可以添加该限制。

然后将它与以下JsonSerializerSettings

 var settings = new JsonSerializerSettings { ContractResolver = LegacyPropertyResolver.Instance }; 

例如:

 [DataContract] class TestObject { [LegacyDataMemberNames("alpha", "omega")] [DataMember(Name = "a")] public int A { get; set; } } public static class JsonExtensions { public static void RenameProperty(this JObject obj, string oldName, string newName) { if (obj == null) throw new NullReferenceException(); var property = obj.Property(oldName); if (property != null) { property.Replace(new JProperty(newName, property.Value)); } } } public class TestClass { public static void Test() { try { TestInner(); } catch (Exception ex) { Debug.Assert(false, ex.ToString()); // No assert throw; } } public static void TestInner() { var test = new TestObject { A = 42 }; var settings = new JsonSerializerSettings { ContractResolver = LegacyPropertyResolver.Instance }; var json = JObject.FromObject(test, JsonSerializer.CreateDefault(settings)); if (json.SelectToken("alpha") != null || json.SelectToken("omega") != null) throw new InvalidOperationException("Failed serialization"); Test(test, json); json.RenameProperty("a", "alpha"); Test(test, json); json.RenameProperty("alpha", "omega"); Test(test, json); } private static void Test(TestObject test, JObject json) { var test1 = json.ToObject(JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = LegacyPropertyResolver.Instance })); if (test1.A != test.A) throw new InvalidOperationException("Failed deserialization"); Console.WriteLine("Successfully deserialized: " + json.ToString(Formatting.None)); Debug.WriteLine("Successfully deserialized: " + json.ToString(Formatting.None)); } } 

我拿了你的代码并将其修改为我自己的样式,如下所示:

  [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple = false, Inherited = true)] public class LegacyDataMemberNamesAttribute : Attribute { public readonly string[] LegacyNames; public LegacyDataMemberNamesAttribute(params string[] legacyNames) { LegacyNames = legacyNames; } } public class LegacyPropertyResolver : DefaultContractResolver { // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." public static readonly LegacyPropertyResolver Instance = new LegacyPropertyResolver(); protected LegacyPropertyResolver() : base() { } protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); foreach (var property in properties.ToArray()) { if (!property.Writable) continue; foreach (var legacyName in GetLegacyNames(property)) { properties.Add(CloneWithLegacyName(property, legacyName)); } } return properties; } static IEnumerable GetLegacyNames(JsonProperty property) { return property.AttributeProvider.GetAttributes(typeof(LegacyDataMemberNamesAttribute), true) .Cast() .SelectMany(a => a.LegacyNames) .Distinct(); } static readonly object[] _emptyObjectArray = new object[0]; static readonly MethodInfo _propertyClone = typeof(JsonProperty).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); static JsonProperty CloneWithLegacyName(JsonProperty property, string legacyName) { var legacyProperty = (JsonProperty)_propertyClone.Invoke(property, _emptyObjectArray); legacyProperty.Readable = false; legacyProperty.PropertyName = legacyName; return legacyProperty; } } 

使用Json.NET的一个非常简单的解决方案是仅提供带有setter的遗留属性。

 class TestObject { public int A { get; set; } public int alpha { set => A = value; } public int omega { set => A = value; } } 

你可能不想让这些公开,在这种情况下你可以只标记private并添加JsonProperty属性。

 class TestObject { public int A { get; set; } [JsonProperty] private int alpha { set => A = value; } [JsonProperty] private int omega { set => A = value; } }