【问题标题】:Serializing Properties With Same Name序列化同名属性
【发布时间】:2021-08-27 10:22:44
【问题描述】:

我的模型结构如下:

public class A
{
    [JsonProperty("id")]
    public string Id { get { return "A:ID"; } }
    
    [JsonProperty("CompKey")]
    public string CompKey { get; set; }
}

public class B : A
{
    [JsonProperty("id")]
    public string Id { get { return "B:ID"; } }
    
    [JsonProperty("name")]
    public string Name { get; set; }
}

我想序列化 B 类的一个实例,但只希望 A 类的 Id 属性可见。 我创建了一个合约解析器来传递给 Newtonsoft:

public class DatabaseEntryResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        property.ShouldSerialize = instance => property.DeclaringType == typeof(A);
        return property;
    }
}

我以下列方式使用它:

var someObject = new A();
var json = JsonConvert.SerializeObject(someObject, new JsonSerializerSettings
{
    ContractResolver = new DatabaseEntryResolver()
});

但是,没有在 JSON 结构中创建任何 ID 属性。任何人都可能对我遇到的这个问题有所了解?

【问题讨论】:

  • 为什么是var someObject = new A(); 而不是var someObject = new B();?序列化B的实例时需要得到A.Id的问题不是吗?

标签: c# json serialization json.net jsonserializer


【解决方案1】:

Json.NET 永远不会序列化两个同名的属性。如果DefaultContractResolver 遇到两个同名的属性,它会选择序列化最不被忽略的派生属性。值得注意的是,此决定基于静态元数据,例如是否存在 [JsonIgnore],而不是基于运行时状态,例如从 ShouldSerialize 返回。

因此,要在序列化B 的实例时使A.Id 取代B.Id,您必须按如下方式覆盖CreateProperty()

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
    var property = base.CreateProperty(member, memberSerialization);
    if (member.ReflectedType == typeof(B)) // Use (typeof(A).IsAssignableFrom(member.ReflectedType)) if you want this to apply to all subclasses of A
    {
        // Make A.Id supersede B.Id
        if (member.Name == nameof(A.Id) && member.DeclaringType != typeof(A))
            property.Ignored = true;
    }
    return property;
}

此外,如果您真的希望在序列化B 的实例时让类 A 的 Id 属性可见,您必须另外删除或忽略 CreateProperties() 中的所有其他内容:

protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
    var properties = base.CreateProperties(type, memberSerialization);
    if (type == typeof(B)) // Use typeof(A).IsAssignableFrom(type)) if you want to only serialize the Id property of A and all its subclasses
    {
        // Remove everything except Id.  You could also Ignore everything except Id.
        properties = properties.Where(p => p.UnderlyingName == nameof(A.Id)).ToList();
    }
    return properties;
}

注意事项:

  • 大概 Newtonsoft 不会检查 ShouldSerialize 来确定要绑定到 JSON 的 Id 属性,因为在反序列化期间无法调用 ShouldSerialize

  • 如果您真的只想为A 的每个实例或A 的任何子类序列化{ "id": "A:ID" },请使用我在代码cmets 中提到的typeof(A).IsAssignableFrom()

  • 您应该用new modifier 标记B.Id 以避免编译警告。

    public class B : A
    {
        [JsonProperty("id")]
        public new string Id { get { return "B:ID"; } } // Fixed compilation
    

    更多信息请见Knowing When to Use Override and New Keywords (C# Programming Guide)

演示小提琴here;

【讨论】:

  • 解释得很好。此解决方案可能有效。按照 Json.Net 上的人的建议让它工作:github.com/JamesNK/Newtonsoft.Json/issues/…
  • 如果您从不想要序列化B.Id,只需用[JsonIgnore] 标记B.Id 即可。我推断您正在使用合同解析器,因此您可以根据运行时的需要选择序列化一个或另一个。
  • 不,实际上那是我非常想做的事情,有时我可能需要序列化 ​​A.Id 有时是 B.Id。现在查看您的代码后,我可以说这正是 Json.Net 的人提出的建议,但是,他们的方法更简单,因为它只是重载了另一个方法,该方法应该执行您在响应中概述的内容。跨度>
【解决方案2】:

对于遇到此问题的人,开发 Netwonsoft.JSON here 的人给出了解释,因此我的新合同解析器如下:

public class DatabaseEntryResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        return base.GetSerializableMembers(objectType).Where(member => member.DeclaringType == typeof(A);
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        property.ShouldSerialize = instance => property.DeclaringType == typeof(A);
        return property;
    }
}

请注意,我仍然决定保留重载的 CreateProperty 方法,尽管从技术上讲它不是必需的,因为重载的 GetSerializableMembers 方法已经过滤掉了我需要的属性。我决定这样做以防万一,但没有它,解决方案仍然有效。

【讨论】:

  • 虽然这可能会满足您的需求,但它并不能完全按照您在问题中所写的那样做。您的问题表明 我想序列化 B 类的实例,但只希望 A 类的 Id 属性可见。 但这所做的是序列化 B 的实例,就好像它是A 的实例。这是一件非常好的事情,只是与所说的略有不同。
猜你喜欢
  • 2013-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-22
  • 2012-01-23
  • 1970-01-01
  • 2019-11-02
相关资源
最近更新 更多