c# - C# 从父类序列化受保护的属性 - 问题
问题描述
我已经实现了一个带有“自定义”ContractResolver 的 JsonSerializer,它覆盖了绑定标志,因此我实际上可以序列化私有成员和受保护成员。因为我想序列化特定类中的对象。让一切都“公开”不是一种选择。
所以我实现了几乎可以正常工作的代码,用于同一类的属性。但是,当涉及到来自父类的受保护属性时,它会在名为“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;
}
}
}
我的父类抽象了必须在每个可序列化对象中引入的 SerializeJson 函数,并提供了导致问题的父属性:
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
}
}
还有我的子类,它实现了实际功能,包括对我已经测试过的东西的一些评论。
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()
因为基类方法包含您最终会忽略的逻辑,例如在属性名称表中缓存属性名称。相反,覆盖
GetSerializableMembers()
andCreateProperty()
。您需要过滤掉索引属性,因为这些属性无法序列化。
您可能希望使用
BindingFlags.FlattenHierarchy
which指定应返回层次结构上的公共和受保护的静态成员。我将您的课程从
CustomJsonSerializerSettings
to重命名为,CustomJsonContractResolver
因为它是一个自定义合同解析器,它被传递到settings 中,而不是设置自己。Newtonsoft 建议静态缓存和重用合约解析器以获得最佳性能。
有关秘密支持字段的更多信息,请参阅是否可以访问自动实现属性后面的支持字段?.
此合同解析程序可能无法正常用于数据合同类型等选择加入合同。如果您的任何类型都有选择加入的合同,您可能需要指定这些应该如何序列化并测试以查看您是否获得了所需的结果。
演示小提琴在这里。
推荐阅读
- flutter - 颤振:未为“未来”类定义运算符“[]”
- spring - 由 BeanInstantiationException 引起的 org.springframework.beans.factory.BeanCreationException
- javascript - 如何在 Bootstrap 4 .btn-group-toggle 上显示 JavaScript 验证错误
- sql - 多个多边形上的多个点 SQL Server Spatial
- python - 使用 any() 函数的 Python 过滤数组
- haskell - 在 Haskell 中设置提示
- weka - 带有 DBSCAN 插件光学 (optics_dbscan) 的 Weka 3.9 - 错误:评估集群时出现问题:null
- c# - ElectronNet.CLI 将版本识别为 2.2,即使我的版本是 2.1
- javascript - 如何将状态中新更新的动态值添加到 React.js 中的旧对象?
- psql - 如何使用 psql 将数据放入数据库