![](/img/trans.png)
[英]C# Xml Serialization: Serialize Class properties as attributes to the parent class
[英]C# Serialize Protected Attributes from parent-class - issue
我已经实现了一个带有“自定义”ContractResolver 的 JsonSerializer,它覆盖了绑定标志,因此我实际上可以序列化私有成员和受保护成员。 因为我想序列化特定类中的对象。 让一切都“公开”不是一种选择。
所以我实现了几乎可以正常工作的代码,用于相同 class 上的属性。 但是,当涉及到来自父类的受保护属性时,它会在名为“k__Backingfield”的 JSON 字符串中生成一些奇怪的额外信息。
首先,结果/问题:
json-debug:
{
"jsonTestPrivate":"child-class",
"jsonTestProtected":"Attr from parent class",
"<jsonTestPrivate>k__BackingField":"child-class" //<<--- I want to get rid of THIS :(
}
这个问题似乎只发生在受父类保护的派生属性上。
--
编码:
namespace XYZ
{
class CustomJsonSerializerSettings : Newtonsoft.Json.Serialization.DefaultContractResolver
{
public CustomJsonSerializerSettings ContractResolver = null;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
}
我的父类抽象了必须在每个可序列化 Object 中引入的 SerializeJson function 并提供了导致问题的父属性:
namespace XYZ
{
public abstract class CommandResponse
{
public abstract string SerializeJson();
protected string jsonTestProtected { get; set; } = "Attr from parent class";
//Helper class to serialize/unserialize objects on higher scope.
//Just derive from this class for every JSON-response/able object and implement abstract function
}
}
我的子类实现了实际的 function,包括对我已经测试过的东西的一些评论。
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Converters;
namespace XYZ
{
class CommandActionInvalid : CommandResponse
{
private string jsonTestPrivate { get; set; } = "child-class";
public override string SerializeJson()
{
DefaultContractResolver resolverObj = new CustomJsonSerializerSettings();
//This attributes dont seem to do anything, even if referenced alot in stackoverflow :(
/*
resolverObj.IgnoreSerializableAttribute = true;
resolverObj.IgnoreSerializableInterface = true;
resolverObj.IgnoreShouldSerializeMembers = true;
resolverObj.SerializeCompilerGeneratedMembers = false;
*/
var settings = new JsonSerializerSettings() { ContractResolver = resolverObj };
return JsonConvert.SerializeObject(this, settings);
}
}
}
k__Backingfield
字段是秘密的、编译器生成的自动实现属性的支持字段。 如果您想在序列化所有其他字段和属性时跳过它们,您的自定义合同解析器可以重写如下:
class CustomJsonContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
const BindingFlags allInstanceMembersFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
IEnumerable<MemberInfo> fields =
objectType.GetFields(allInstanceMembersFlags)
// Skip k__BackingField secret backing fields of auto-implemented properties.
.Where(fi => !fi.IsDefined(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)));
IEnumerable<MemberInfo> props = objectType.GetProperties(allInstanceMembersFlags)
// Skip indexed properties like List[0].
.Where(pi => pi.GetIndexParameters().Length == 0);
return fields.Concat(props).ToList();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
switch (member)
{
case FieldInfo fi:
// Passing MemberSerialization.Fields causes the JsonProperty to be marked as readable and writable
return base.CreateProperty(fi, MemberSerialization.Fields);
case PropertyInfo pi:
var jsonProperty = base.CreateProperty(pi, memberSerialization);
// Mark the property as readable and writable if the corresponding get and set methods exist even if not public
if (pi.GetGetMethod(true) != null)
jsonProperty.Readable = true;
if (pi.GetSetMethod(true) != null)
jsonProperty.Writable = true;
return jsonProperty;
default:
return base.CreateProperty(member, memberSerialization);
}
}
}
笔记:
检查字段是否为秘密支持字段的方法取自Marc Gravell的这个答案,以确定 FieldInfo 是否是编译器生成的支持字段。
如果存在相应的 get- 和 set- 方法,则只应设置JsonProperty.Readable
和JsonProperty.Writable
。 如果您无条件地设置它们,您的代码将在序列化具有仅设置属性的类型时崩溃,例如
protected string jsonTestProtectedSetOnlyProperty { set { jsonTestProtected = value; } }
我不建议完全替换CreateProperties()
的逻辑,因为基本 class 方法包含您最终会忽略的逻辑,例如在属性名称表中缓存属性名称。
您需要过滤掉索引属性,因为这些属性无法序列化。
您可能希望使用BindingFlags.FlattenHierarchy
指定应返回层次结构上的公共和受保护 static 成员。
我将您的 class 从CustomJsonSerializerSettings
重命名为CustomJsonContractResolver
因为它是一个自定义合同解析器,它被传递到settings 中,而不是设置自己。
Newtonsoft 建议静态缓存和重用合约解析器以获得最佳性能。
有关秘密支持字段的更多信息,请参阅是否可以访问自动实现属性后面的支持字段? .
此合同解析程序可能无法正常用于数据合同类型等选择加入合同。 如果您的任何类型都有选择加入的合同,您可能需要指定如何序列化这些合同并进行测试以查看您是否获得了所需的结果。
演示小提琴在这里。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.