IgnoreDataMember不起作用,但JsonIgnore确实如此

时间:2016-06-24 19:16:34

标签: c# serialization json.net .net-4.5

我遇到了一个奇怪的情况,IgnoreDataMember没有做这项工作,但JsonIgnore确实如此。

在这种情况下,我会从具有public {get; set;}属性的类继承,并且我选择使用NotSupportedException覆盖setter。我只希望序列化程序能够设置属性,而不是用户,但我希望用户得到它,所以我已经完成了以下工作:

[DataContract]
public class BaseObject : BaseInterface
{
     [DataMember]
     public virtual double information { get; set; }
}

[DataContract]
public class ServerGeneratedObject : BaseObject
{
     [IgnoreDataMember]
     public override double information {
         get { return server_set_information; }
         set { throw new NotSupportedException("Server generated property"); }
     }

     [DataMember(Name="information")]
     private double server_set_information { get; set; }
}

不幸的是,这会引发错误"名为'信息的成员'已经存在于' ServerGeneratedObject'。使用JsonPropertyAttribute指定另一个名称。"

但是,如果我使用[JsonIgnore]属性,则按预期工作。这似乎是由于数据合同解析程序的这一部分(code currently lives here):

bool flag2 = JsonTypeReflector.GetAttribute<JsonIgnoreAttribute>(attributeProvider) != null ||
    JsonTypeReflector.GetAttribute<JsonExtensionDataAttribute>(attributeProvider) != null ||
    JsonTypeReflector.GetAttribute<NonSerializedAttribute>(attributeProvider) != null;
if (memberSerialization != MemberSerialization.OptIn)
{
    bool flag3 = JsonTypeReflector.GetAttribute<IgnoreDataMemberAttribute>(attributeProvider) != null;
    property.Ignored = flag2 | flag3;
}

该属性未正确设置为“忽略”&#39;因为它在&#39; OptIn&#39;模式,但如果是这样,我不知道为什么继承的&#34;信息&#34;财产正在选择&#34;因为&#34; DataMember&#34;属性不应该是可继承的。我提出了一个错误here,以防万一这不是预期的行为。

我能在这做些什么吗?我试图避免使用任何&#34; Newtonsoft&#34;我的公共数据模型上的属性,因为我不希望使用我的客户端库对象模型的人必须引用Newtonsoft程序集。

2 个答案:

答案 0 :(得分:1)

怀疑在您使用DataContractJsonSerializer和{{1标记属性的情况下,您所看到的逻辑旨在使Json.NET与[DataContract]保持一致}}。完成此操作后,[IgnoreDataContract]将优先,数据合同序列化程序将输出该属性。例如。序列化

[DataContract]

[DataContract] public class Test { [DataMember] [IgnoreDataMember] public virtual double information { get; set; } } 中的结果。

(逻辑也可能是效率调整。在简单的情况下,如果某个类型标有{"information":0},则[DataContract]是多余的,因此无需花时间检查反思它。)

也许正因如此, Json.NET和[IgnoreDataMember] 都会抛出一个异常序列化派生类,该派生类覆盖其基类中的数据成员属性,用{标记重写的属性{1}},然后添加一个具有相同数据成员名称的无关属性。如果您尝试这样做,Json.NET会抛出您看到的异常 - DataContractJsonSerializer也会抛出异常:

[IgnoreDataMember]

尽管如此,您可以通过创建继承自custom contract resolverDefaultContractResolverCamelCasePropertyNamesContractResolver来覆盖CreateProperty(),从而使Json.NET按照需要运行 并添加所需的逻辑:

DataContractJsonSerializer

然后使用它:

System.Runtime.Serialization.SerializationException occurred
  Message="The data contract type 'Question38020614.ServerGeneratedObject' is not serializable with DataContractJsonSerializer because the data member 'information' is duplicated in its type hierarchy."
  Source="System.ServiceModel.Web"

答案 1 :(得分:0)

我遇到了一个上述解决方案无法使用的问题,因为如果您仅在创建属性后“忽略”该属性,则如果您忽略的属性与另一个属性具有相同的名称,Newtonsoft仍然会崩溃尝试序列化。通过完全不“创建” JsonProperty(将其添加到属性字典中),可以避免这种情况:

/// <summary>Properties tagged with the system <see cref="IgnoreDataMemberAttribute"/>
/// should be ignored by the JSON serializer.
/// Due to a Newtonsoft JSON bug (https://github.com/JamesNK/Newtonsoft.Json/issues/943)
/// We need to use their own specific JsonIgnore attribute to effectively ignore a property.
/// This contract resolver aims to correct that.</summary>
public class RespectIgnoreDataMemberResolver : DefaultContractResolver
{
    /// <inheritdoc/>
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        return base.GetSerializableMembers(objectType)
            .Where(pi => !pi.IsAttributeDefinedFast<IgnoreDataMemberAttribute>())
            .ToList();
    }

    /// <inheritdoc/>
    protected override JsonProperty CreateProperty(MemberInfo member,
        MemberSerialization memberSerialization)
    {
        if (member.IsAttributeDefinedFast<IgnoreDataMemberAttribute>())
            return null;
        return base.CreateProperty(member, memberSerialization);
    }
}

IsAttributeDefinedFast只是我在其他地方可以缓存结果以备将来使用的一种类型扩展方法:

    #region Cached Attribute Retrieval
    /// <summary>Cache of attributes retrieved for members.</summary>
    private static readonly ConcurrentDictionary<Tuple<string, Type, Type>, object>
        CachedPropertyAttributes = new ConcurrentDictionary<Tuple<string, Type, Type>, object>();

    /// <summary>Determines whether the member has the specified attribute defined.</summary>
    /// <typeparam name="T">The type of the attribute to look for.</typeparam>
    /// <param name="member">The member to check if an attribute is defined on.</param>
    /// <returns>True if the attribute is defined.</returns>
    public static bool IsAttributeDefinedFast<T>(this MemberInfo member)
    {
        return IsAttributeDefinedFast(member, typeof(T));
    }

    /// <summary>Determines whether the member has the specified attribute defined.</summary>
    /// <param name="member">The member to check if an attribute is defined on.</param>
    /// <param name="attributeType">The type of the attribute to look for.</param>
    /// <returns>True if the attribute is defined.</returns>
    public static bool IsAttributeDefinedFast(this MemberInfo member, Type attributeType)
    {
        return member.GetCustomAttributeFast(attributeType) != null;
    }

    /// <summary>Gets the specified attribute from the member.</summary>
    /// <typeparam name="T">The type of the attribute to look for.</typeparam>
    /// <param name="member">The member to get the custom attribute of.</param>
    /// <returns>True if the attribute is defined on the specified member.</returns>
    public static T GetCustomAttributeFast<T>(this MemberInfo member)
    {
        return (T)GetCustomAttributeFast(member, typeof(T));
    }

    /// <summary>Gets the specified attribute from the member.</summary>
    /// <param name="member">The member to get the custom attribute of.</param>
    /// <param name="attributeType">The type of the attribute to look for.</param>
    /// <returns>True if the attribute is defined on the specified member.</returns>
    public static object GetCustomAttributeFast(this MemberInfo member, Type attributeType)
    {
        Tuple<string, Type, Type> cacheKey =
            Tuple.Create(member.Name, member.DeclaringType, attributeType);
        object result = CachedPropertyAttributes.GetOrAdd(cacheKey, key =>
        {
            try { return Attribute.GetCustomAttribute(member, attributeType, true); }
            catch (Exception ex) { return ex; }
        });
        if (result is Exception exceptionResult)
            throw exceptionResult;
        return result;
    }
    #endregion Cached Attribute Retrieval