我遇到了一个奇怪的情况,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程序集。
答案 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 resolver或DefaultContractResolver
的CamelCasePropertyNamesContractResolver
来覆盖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