从传统的属性名称JSON反序列化 [英] json deserialize from legacy property names

查看:124
本文介绍了从传统的属性名称JSON反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何设置Newtonsoft.Json使用传统会员名反序列化对象,但使用当前成员名称序列呢?

How can I setup Newtonsoft.Json to deserialize an object using legacy member names but serialize it using the current member name?

下面是一个需要是一个例子对象序列化和反序列化。我给一个属性包含,它可能已根据过去连载的名称列表的属性。

Here's an example object that needs to be serialized and deserialized. I've given a property an attribute containing a list of names that it may have been serialized under in the past.

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



我想总是使用名称一,但是JSON序列化能够反序列化从任何遗留的名字一个属性,包括阿尔法和欧米茄,以及当前的名称,A

I'd like to json serialize always using name "a" but be able to deserialize to the one property from any legacy name including "alpha" and "omega" as well as the current name, "a"

推荐答案

这可以用自定义 IContractResolver <完成/ code> ,通过扩展预先存在的解析器之一,例如创建的 DefaultContractResolver

This can be done with a custom IContractResolver, created by extending one of the pre-existing resolvers, for instance 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<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;
    }
}

请注意,此实现不需要类有明确的数据合同属性注释。可以添加一个限制,如果你喜欢。

Note that this implementation doesn't require that the class have explicit data contract attribute annotation. You could add that restriction, if you prefer.

然后用下面的 JsonSerializerSettings

Then use it with the following 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<TestObject>(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));
    }
}

这篇关于从传统的属性名称JSON反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆