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

Posted

技术标签:

【中文标题】json 从遗留属性名称反序列化【英文标题】:json deserialize from legacy property names 【发布时间】:2016-01-14 07:40:44 【问题描述】:

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

编辑:要求是从正在序列化/反序列化的类中删除过时的成员。

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

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

我希望始终使用名称“a”进行 json 序列化,但能够从任何旧名称(包括“alpha”和“omega”以及当前名称“a”)反序列化为一个属性

【问题讨论】:

这个问题不应该被关闭。 @Brian Rogers 提出的“类似”问题有一个解决方案,包括让过时的成员留在课堂上。这个问题是关于删除过时的成员。 【参考方案1】:

这可以通过扩展DefaultContractResolver 创建的自定义IContractResolver 来完成:

[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);

注意事项:

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

您应该cache and reuse instances of contract resolvers 以获得最佳性能。

演示小提琴here.

【讨论】:

天哪,哇,这是一个非常彻底的答案。谢谢。【参考方案2】:

使用 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; 

【讨论】:

很好的回答蒂姆【参考方案3】:

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

    [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 从遗留属性名称反序列化的主要内容,如果未能解决你的问题,请参考以下文章

Json 使用动态属性名称反序列化对象数组

序列化和反序列化过程中 JSON 属性的不同名称

如何根据json中的属性编写jackson反序列化器

将 JSON 包反序列化为具有自定义属性名称的类

golang中序列化和反序列化时JSON属性的不同名称

反序列化以 @ 开头的 JSON 属性