【问题标题】:IgnoreDataMember doesn't work, but JsonIgnore doesIgnoreDataMember 不起作用,但 JsonIgnore 起作用
【发布时间】:2016-06-24 19:16:34
【问题描述】:

我遇到了一个奇怪的情况,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' 上已存在名为 'information' 的成员。使用 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;
}

该属性未正确设置为“忽略”,因为它处于“选择加入”模式,但如果是这种情况,我不知道为什么继承的“信息”属性被“选择加入”,因为“DataMember” " 属性不应该是可继承的。我提交了一个错误here,以防这不是预期的行为。

有什么我可以在这里做的吗?我试图避免在我的公共数据模型上使用任何“Newtonsoft”属性,因为我不希望使用我的客户端库对象模型的人必须引用 Newtonsoft 程序集。

【问题讨论】:

  • 我刚刚尝试使用DataContractJsonSerializer 序列化您的类,但它引发了异常数据协定类型“Question38020614.ServerGeneratedObject”不能使用 DataContractJsonSerializer 序列化,因为数据成员“信息”是在其类型层次结构中重复。

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


【解决方案1】:

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

[DataContract]
public class Test
{
    [DataMember]
    [IgnoreDataMember]
    public virtual double information { get; set; }
}

结果为@​​987654330@。

(逻辑也可能是一种效率调整。在简单的情况下,如果一个类型标记为[DataContract],那么[IgnoreDataMember] 是多余的,因此无需花时间通过反射检查它。)

也许正因为如此,Json.NET 和DataContractJsonSerializer 都会抛出一个异常序列化一个派生类,该类会覆盖其基类中的数据成员属性,并用[IgnoreDataMember] 标记被覆盖的属性,然后添加一个具有相同数据成员名称的不相关属性。如果你尝试这样做,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"

尽管如此,您可以通过创建一个继承自 DefaultContractResolverCamelCasePropertyNamesContractResolvercustom contract resolver,覆盖 CreateProperty() 来使 Json.NET 行为符合要求 并添加所需的逻辑:

public class IgnoreDataMemberContractResolver : 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."
    // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
    static IgnoreDataMemberContractResolver instance;

    // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
    static IgnoreDataMemberContractResolver() { instance = new IgnoreDataMemberContractResolver(); }

    public static IgnoreDataMemberContractResolver Instance { get { return instance; } }

    protected override JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (memberSerialization == MemberSerialization.OptIn)
        {
            // Preserve behavior that [DataMember] supersedes [IgnoreDataMember] when applied in the same type
            // but not when appled to a base type.
            if (!property.Ignored
                && property.AttributeProvider.GetAttributes(typeof(IgnoreDataMemberAttribute), false).Any()
                && !property.AttributeProvider.GetAttributes(typeof(DataMemberAttribute), true).Any())
            {
                property.Ignored = true;
            }
        }
        return property;
    }
}

然后像这样使用它:

var settings = new JsonSerializerSettings { ContractResolver = IgnoreDataMemberContractResolver.Instance };
var json = JsonConvert.SerializeObject(serverGeneratedObject, settings);

【讨论】:

    【解决方案2】:

    我遇到了上述解决方案不起作用的问题,因为如果您只是在创建属性后“忽略”该属性,如果您忽略的属性与您正在使用的另一个属性同名,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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-22
      • 2013-08-11
      • 1970-01-01
      • 1970-01-01
      • 2020-07-03
      • 1970-01-01
      • 2011-02-26
      相关资源
      最近更新 更多