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

46
我们在一个MVC3项目中,使用Microsoft的JSON序列化器和JSON.NET时遇到了问题。
众所周知,DateTime在Microsoft的序列化器中基本上是有问题的,因此我们切换到JSON.NET以避免这个问题。 这很好用,但是我们正在尝试序列化的一些类是带有DataContract / DataMember属性的POCO类。 它们定义在被多处引用的程序集中。 此外,它们还具有其他未标记为DataMembers的显示属性,例如Customer。
[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属性,但是DateTime类型会出现问题。我需要JSON.NET忽略这个类具有DataContract/DataMember属性的事实,并像如果它们不存在一样执行标准的公共字段序列化。

1
你解决了吗?我遇到了完全相同的问题,需要找到解决办法。 - Kendall Bennett
3
最终我为Json.Net添加了JsonProperty属性。 - Nick
6个回答

42

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

[DataContract]
[JsonObject(MemberSerialization.OptOut)]

1
非常感谢。这应该被标记为正确答案。我正在处理一个非常老的应用程序(WCF与RESTFul API),并希望序列化一些数据以传递给较新的Web API服务。这个解决方案非常有效。点赞。 - DragoRaptor
谢谢。这个解决方案以大锤的方式很好地解决了我的问题。我可以看到在下一个答案中使用[JsonProperty]属性来获得更多细节。 - Wade Hatler
不幸的是,如果您还想为 JSON 启用选择性序列化,[DataMember] 属性将导致该属性也被序列化为 JSON。如果不需要这样做,您必须为每个带有 [DataMember] 属性的属性添加 [JsonIgnore],或使用涉及自定义合同解析器的解决方案之一。 - Florian Winter

24

正如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()
});

希望这可以帮到你!


15

我曾经遇到过一个几乎和你所遇到的问题相关的问题,并通过查看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()
});

2
好的。为什么不行呢?有什么问题吗?让我们帮助你。 - Cornelius
这种方法对我也不起作用,Amry,你还有什么要补充的吗? - Scott
这个答案对于2012年的Json.NET版本对我来说是有效的。我不知道它现在在2017年是否仍然有效(似乎已经不再有效),遗憾的是我现在在2017年没有个人需要使用它,因此我不会建议一个可行的解决方案。 - Amry

10
根据Json.NET文档,如果属性还带有Json.NET特定属性(例如[JsonProperty]),则会忽略[DataMember]属性。有关详细信息,请参见Serialization Attributes documentation:

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

文档仅涵盖名称属性,但从我的经验来看,[JsonProperty]属性也完全遮盖了[DataMember]属性所做的设置。因此,如果您的情况可行,请为应忽略[DataMember]注释的属性添加Json.NET属性。

3
从您的回答中,我了解到如果一个类有**[DataContract]属性,则它将成员序列化设置为选择加入,这意味着任何成员(_字段或属性_)必须具有JsonProperty或[DataMember]**属性才能被序列化(_否则将被忽略_)。我花了几个小时才找到这些信息。 - stomy

9
如果您想忽略所有类型的DataContractAttribute的存在,而不必添加其他属性,则自定义合同解析器是正确的解决方案。然而,从Json.NET 9.0.1开始,Amry的解析器不再起作用。Doolali的解析器可以工作,但它会有额外的副作用,即序列化所有公共属性,包括那些标记为[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://dev59.com/n2oy5IYBdhLWcg3wcNhO
            // 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;
        }
    }
}

你可能想要缓存合约解析器以获得最佳性能

1
@HappyNomad - DefaultContractResolver 没有提供一种简单的方法来注入属性。我重写了 CreateProperties() 方法来传递 OptOut,但是 contract.MemberSerialization 的值仍然返回为 OptIn,因为这似乎是不一致的,所以我选择重写了 CreateObjectContract() 方法。 - dbc
1
修复得不错,但我注意到每个类型都会被反复调用 RemoveDataContractAttributeMemberSerialization。这是因为 base.CreateObjectContract 调用了 CreateProperties。我在 JSON.NET 上提交了一个问题 an issue - HappyNomad
1
是的,我在发表评论后立即注意到这一点,所以我重写了我的评论。 - HappyNomad

-3

2
我想要做的是相反的,不是忽略该字段,而是在使用JSON.NET进行序列化时包含该字段。 - Nick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接