json 从遗留 属性 名称反序列化
json deserialize from legacy property names
如何设置 Newtonsoft.Json 使用旧成员名称反序列化对象,但使用当前成员名称对其进行序列化?
编辑:要求从 class 中删除过时的成员 serialized/deserialized。
这是一个需要序列化和反序列化的示例对象。我给了一个 属性 一个属性,其中包含一个名称列表,它可能在过去被序列化过。
[DataContract]
class TestObject {
[LegacyDataMemberNames("alpha", "omega")]
[DataMember(Name = "a")]
public int A { get; set; }
}
我想 json 始终使用名称 "a" 进行序列化,但能够从任何旧名称(包括 "alpha" 和 [=] 反序列化为 属性 25=] 以及当前名称,"a"
这可以通过自定义 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;
}
}
然后将属性添加到您的类型中,如问题所示:
[DataContract]
class TestObject
{
[LegacyDataMemberNames("alpha", "omega")]
[DataMember(Name = "a")]
public int A { get; set; }
}
构建并配置 LegacyPropertyResolver
的实例,例如如下:
static IContractResolver legacyResolver = new LegacyPropertyResolver
{
// Configure as required, e.g.
// NamingStrategy = new CamelCaseNamingStrategy()
};
然后在settings
中使用:
var settings = new JsonSerializerSettings { ContractResolver = legacyResolver };
var deserialized = JsonConvert.DeserializeObject<TestObject>(jsonString, settings);
备注:
此实现不要求 class 具有显式数据协定属性注释。如果您愿意,可以添加该限制。
您应该 cache and reuse instances of contract resolvers 以获得最佳性能。
演示 fiddle here.
我使用了您的代码并将其修改为我自己的样式,如下所示:
[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<JsonProperty> 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<string> GetLegacyNames(JsonProperty property) {
return property.AttributeProvider.GetAttributes(typeof(LegacyDataMemberNamesAttribute), true)
.Cast<LegacyDataMemberNamesAttribute>()
.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; }
}
您可能不想拥有这些 public,在这种情况下,您可以标记 private
并添加 JsonProperty
属性。
class TestObject {
public int A { get; set; }
[JsonProperty] private int alpha { set => A = value; }
[JsonProperty] private int omega { set => A = value; }
}
如何设置 Newtonsoft.Json 使用旧成员名称反序列化对象,但使用当前成员名称对其进行序列化?
编辑:要求从 class 中删除过时的成员 serialized/deserialized。
这是一个需要序列化和反序列化的示例对象。我给了一个 属性 一个属性,其中包含一个名称列表,它可能在过去被序列化过。
[DataContract]
class TestObject {
[LegacyDataMemberNames("alpha", "omega")]
[DataMember(Name = "a")]
public int A { get; set; }
}
我想 json 始终使用名称 "a" 进行序列化,但能够从任何旧名称(包括 "alpha" 和 [=] 反序列化为 属性 25=] 以及当前名称,"a"
这可以通过自定义 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;
}
}
然后将属性添加到您的类型中,如问题所示:
[DataContract]
class TestObject
{
[LegacyDataMemberNames("alpha", "omega")]
[DataMember(Name = "a")]
public int A { get; set; }
}
构建并配置 LegacyPropertyResolver
的实例,例如如下:
static IContractResolver legacyResolver = new LegacyPropertyResolver
{
// Configure as required, e.g.
// NamingStrategy = new CamelCaseNamingStrategy()
};
然后在settings
中使用:
var settings = new JsonSerializerSettings { ContractResolver = legacyResolver };
var deserialized = JsonConvert.DeserializeObject<TestObject>(jsonString, settings);
备注:
此实现不要求 class 具有显式数据协定属性注释。如果您愿意,可以添加该限制。
您应该 cache and reuse instances of contract resolvers 以获得最佳性能。
演示 fiddle here.
我使用了您的代码并将其修改为我自己的样式,如下所示:
[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<JsonProperty> 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<string> GetLegacyNames(JsonProperty property) {
return property.AttributeProvider.GetAttributes(typeof(LegacyDataMemberNamesAttribute), true)
.Cast<LegacyDataMemberNamesAttribute>()
.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; }
}
您可能不想拥有这些 public,在这种情况下,您可以标记 private
并添加 JsonProperty
属性。
class TestObject {
public int A { get; set; }
[JsonProperty] private int alpha { set => A = value; }
[JsonProperty] private int omega { set => A = value; }
}