配置 JSON.NET 以忽略 DataContract/DataMember 属性

Posted

技术标签:

【中文标题】配置 JSON.NET 以忽略 DataContract/DataMember 属性【英文标题】:Configure JSON.NET to ignore DataContract/DataMember attributes 【发布时间】:2012-06-18 19:00:55 【问题描述】:

我们在使用 Microsoft JSON 序列化程序和 JSON.NET 的 MVC3 项目中遇到了情况。

每个人都知道微软的序列化器中的 DateTime 基本上被破坏了,所以我们切换到 JSON.NET 来避免这个问题。这很好用,除了我们尝试序列化的一些类是具有 DataContract/DataMember 属性的 POCO。它们是在一个在多个位置引用的程序集中定义的。此外,它们还有一些其他显示属性未标记为 DataMembers 以提高效率。例如,客户

[DataContract]
public class Customer

   [DataMember]
   public string FirstName  get; set;
   [DataMember]
   public string LastName  get; set;
   public string FullName 
   
       get
         return FirstName + " " + LastName; 
   


当此客户通过 WCF 传递时,客户端可以引用该程序集并使用 FullName 就好了,但是当使用 JSON.NET 序列化时,它会发现 FullName 不是 [DataMember] 并且不会序列化它。是否有一个选项可以传递给 JSON.NET 来告诉它忽略一个类应用了[DataContract] 属性的事实?

注意: 在 .NET 中使用 javascriptSerializer 可以很好地用于 FullName 属性,但 DateTimes 已损坏。我需要 JSON.NET 来忽略此类具有 DataContract/DataMember 属性的事实,并且只执行标准的公共字段序列化,就像它们不存在时一样。

【问题讨论】:

您解决了吗?我遇到了完全相同的问题,需要找到解决方案 我最终为 Json.Net 添加了 JsonProperty 属性 【参考方案1】:

只需使用 Json.Net 的 OptOut 属性。它将优先于 DataContract。

[DataContract]
[JsonObject(MemberSerialization.OptOut)]

【讨论】:

非常感谢。这应该被标记为正确答案。我正在开发一个非常旧的应用程序(带有 RESTFul api 的 WCF),并希望序列化一些数据以将其传递给更新的 Web api 服务。这个解决方案就像一个魅力。投票赞成。 谢谢。这以一种大锤的方式很好地解决了我的问题。我可以看到在下一个答案中使用 [JsonProperty] 属性以获得更多粒度。 不幸的是,如果您还想为 JSON 选择加入序列化,[DataMember] 属性将导致该属性也被序列化为 JSON。如果这是不需要的,您必须将 [JsonIgnore] 添加到具有 [DataMember] 属性的每个属性,或使用涉及自定义合同解析器的解决方案之一。【参考方案2】:

正如 Amry 所说,您可以使用自己的 IContractResolver。

不幸的是,Amry 提供的解决方案对我不起作用,以下是我设法得到的解决方案:

public class AllPropertiesResolver : DefaultContractResolver

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        //property.HasMemberAttribute = true;
        property.Ignored = false;

        //property.ShouldSerialize = instance =>
        //
        //    return true;
        //;

        return property;
    

有几行注释,这些不是使我的解决方案工作所必需的,但你永远不知道!

这与Amry的解决方案用法相同:

var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings 
    ContractResolver = new AllPropertiesResolver()
);

希望这会有所帮助!

【讨论】:

【参考方案3】:

我遇到了一个几乎与您所遇到的问题相关的问题,并通过查看 Json.NET 的代码设法找到了解决方案。所以它可能不是最好的解决方案,但它对我有用。

为此,您需要实现自己的IContractResolver。一个过度简化的实现,包括所有参数并忽略所有属性(不仅仅是DataContract,还有其他内置的 Json.NET 规则,所以你设置的任何最初应该影响成员选择的选项现在都被覆盖了这段代码):

class AllPropertiesResolver : DefaultContractResolver

    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    
        return objectType.GetProperties()
            .Where(p => p.GetIndexParameters().Length == 0)
            .Cast<MemberInfo>()
            .ToList();
    

下面是代码使用示例:

var json = JsonConvert.SerializeObject(result, new JsonSerializerSettings 
    ContractResolver = new AllPropertiesResolver()
);

【讨论】:

太棒了。为什么不?什么不工作?帮助我们帮助您。 这种方法对我也不起作用,Amry,你还需要添加什么才能让它起作用吗? 这个答案在 2012 年适用于 Json.NET 的版本。我不知道它现在是否可以在 2017 年工作(似乎它不再工作了),很遗憾我现在在 2017 年没有个人需要使用它,所以我不会提出可行的解决方案。【参考方案4】:

根据 Json.NET 文档,[DataMember] 属性将被忽略,如果属性也使用 Json.NET 特定属性(例如 [JsonProperty])进行注释。有关详细信息,请参阅 Serialization Attributes documentation:

Json.NET 属性优先于标准 .NET 序列化属性,例如如果属性上同时存在 JsonPropertyAttribute 和 DataMemberAttribute 并且都自定义了名称,则将使用 JsonPropertyAttribute 中的名称。

文档仅涵盖 name 属性,但根据我的经验,[JsonProperty] 属性也完全隐藏了由[DataMember] 属性完成的设置。因此,如果对您的情况可行,还可以将 Json.NET 属性添加到应忽略 [DataMember] 注释的属性中。

【讨论】:

从您的回答中,我了解到如果一个类具有 [DataContract] 属性,那么它将成员序列化设置为 opt-in 这意味着任何成员(字段或属性必须具有要序列化的 JsonProperty 或 [DataMember] 属性(否则将被忽略)。我花了几个小时才找到这些信息。【参考方案5】:

如果您想忽略所有类型的DataContractAttribute 的存在而不必添加其他属性,那么custom contract resolver 是正确的解决方案。但是,从 Json.NET 9.0.1 Amry's resolver 开始不再有效。 Doolali's resolver 有效,但它具有序列化所有公共属性的额外副作用,包括标记为[JsonIgnore] 的那些。如果您需要一个忽略DataContractAttribute的存在但在其他方面表现得像默认合同解析器的合同解析器,则可以使用以下内容:

public class IgnoreDataContractContractResolver : DefaultContractResolver

    static MemberSerialization RemoveDataContractAttributeMemberSerialization(Type type, MemberSerialization memberSerialization)
    
        if (memberSerialization == MemberSerialization.OptIn)
        
            type = Nullable.GetUnderlyingType(type) ?? type;

            // Json.NET interprets DataContractAttribute as inherited despite the fact it is marked with Inherited = false
            // https://json.codeplex.com/discussions/357850
            // https://***.com/questions/8555089/datacontract-and-inheritance
            // https://github.com/JamesNK/Newtonsoft.Json/issues/603
            // Thus we need to manually climb the type hierarchy to see if one is present.

            var dataContractAttribute = type.BaseTypesAndSelf().Select(t => t.GetCustomAttribute<DataContractAttribute>()).FirstOrDefault(a => a != null);
            var jsonObjectAttribute = type.GetCustomAttribute<JsonObjectAttribute>();

            if (dataContractAttribute != null && jsonObjectAttribute == null)
                memberSerialization = MemberSerialization.OptOut;
        
        return memberSerialization;
    

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    
        var properties = base.CreateProperties(type, RemoveDataContractAttributeMemberSerialization(type, memberSerialization));
        return properties;
    

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    
        var contract = base.CreateObjectContract(objectType);
        contract.MemberSerialization = RemoveDataContractAttributeMemberSerialization(objectType, contract.MemberSerialization);
        return contract;
    


public static class TypeExtensions

    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    
        while (type != null)
        
            yield return type;
            type = type.BaseType;
        
    

您可能想cache the contract resolver for best performance。

【讨论】:

@HappyNomad - DefaultContractResolver 没有提供注入属性的简单方法。我覆盖CreateProperties() 以传入OptOut——但contract.MemberSerialization 的值仍然以OptIn 的形式返回。由于这似乎不一致,我也选择了覆盖CreateObjectContract() 很好的解决方法,但我注意到RemoveDataContractAttributeMemberSerialization 浪费地被每种类型调用两次。这是因为base.CreateObjectContract 调用CreateProperties。我为此向 JSON.NET 提交了an issue。 是的,我在发表评论后立即注意到了这一点,因此重写了我的评论。【参考方案6】:

你试过吗?

IgnoreDataMemberAttribute

【讨论】:

我想做与忽略字段相反的事情,我希望在使用 JSON.NET 进行序列化时包含该字段

以上是关于配置 JSON.NET 以忽略 DataContract/DataMember 属性的主要内容,如果未能解决你的问题,请参考以下文章

忽略 Json.net 中的空字段

Json.Net 自定义 ContractResolver 忽略自定义对象

使用新的 Net Core 3.0 Json 时忽略属性

在 JSON.NET 数据解析期间忽略解析错误

Json.net 忽略实体某些属性的序列化

为啥当我使用 JSON.NET 反序列化时会忽略我的默认值?