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

bbo*_*234 6 c# json json.net json-deserialization

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

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

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

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

Tim*_*ers 7

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

class TestObject {
    public int A { get; set; }
    public int alpha { set => A = value; }
    public int omega { set => A = value; }
}
Run Code Online (Sandbox Code Playgroud)

您可能宁愿不公开这些,在这种情况下,您只需标记private并添加JsonProperty属性即可。

class TestObject {
    public int A { get; set; }
    [JsonProperty] private int alpha { set => A = value; }
    [JsonProperty] private int omega { set => A = value; }
}
Run Code Online (Sandbox Code Playgroud)


dbc*_*dbc 5

这可以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<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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

然后使用以下内容JsonSerializerSettings:

var settings = new JsonSerializerSettings { ContractResolver = LegacyPropertyResolver.Instance };
Run Code Online (Sandbox Code Playgroud)

例如:

[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));
    }
}
Run Code Online (Sandbox Code Playgroud)