[英]C# Serialize Protected Attributes from parent-class - issue
I have implemented a JsonSerializer with a "custom" ContractResolver which overrides the binding-flags so I can actually serialize private and protected members.我已经实现了一个带有“自定义”ContractResolver 的 JsonSerializer,它覆盖了绑定标志,因此我实际上可以序列化私有成员和受保护成员。 As I want to serialize objects in specific classes.
因为我想序列化特定类中的对象。 Making everything just "public" is not an option.
让一切都“公开”不是一种选择。
So I implemented code which works almost well, for attributes on same class.所以我实现了几乎可以正常工作的代码,用于相同 class 上的属性。 However when there are protected attrs from the parent-class are involved, it generates some strange extra information in JSON-string called "k__Backingfield".
但是,当涉及到来自父类的受保护属性时,它会在名为“k__Backingfield”的 JSON 字符串中生成一些奇怪的额外信息。
First of all, the result/problem:首先,结果/问题:
json-debug:
{
"jsonTestPrivate":"child-class",
"jsonTestProtected":"Attr from parent class",
"<jsonTestPrivate>k__BackingField":"child-class" //<<--- I want to get rid of THIS :(
}
This problem ONLY seems to occur for derived attributed that are protected in parent-class.这个问题似乎只发生在受父类保护的派生属性上。
-- --
The code:编码:
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;
}
}
}
My parent-class which abstracts the SerializeJson function that must be introduced in every Serializable Object and supplied parent attributes which make the problem:我的父类抽象了必须在每个可序列化 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
}
}
And my child-class which implements the actual function, including some comments on things that I have already tested.我的子类实现了实际的 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);
}
}
}
The k__Backingfield
fields are the secret, compiler-generated backing fields for auto-implemented properties. k__Backingfield
字段是秘密的、编译器生成的自动实现属性的支持字段。 If you want to skip them while serializing all other fields and properties, your custom contract resolver can be rewritten as follows:如果您想在序列化所有其他字段和属性时跳过它们,您的自定义合同解析器可以重写如下:
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);
}
}
}
Notes:笔记:
The method to check whether a field is a secret backing field is taken from this answer by Marc Gravell to Determine if FieldInfo is compiler generated backingfield .检查字段是否为秘密支持字段的方法取自Marc Gravell的这个答案,以确定 FieldInfo 是否是编译器生成的支持字段。
You should only set JsonProperty.Readable
and JsonProperty.Writable
if the corresponding get- and set- methods exist.如果存在相应的 get- 和 set- 方法,则只应设置
JsonProperty.Readable
和JsonProperty.Writable
。 If you set them unconditionally your code will crash serializing a type with a set-only property such as如果您无条件地设置它们,您的代码将在序列化具有仅设置属性的类型时崩溃,例如
protected string jsonTestProtectedSetOnlyProperty { set { jsonTestProtected = value; } }
I don't recommend completely replacing the logic of CreateProperties()
as the base class method contains logic that you end up omitting, eg to cache property names in property name tables.我不建议完全替换
CreateProperties()
的逻辑,因为基本 class 方法包含您最终会忽略的逻辑,例如在属性名称表中缓存属性名称。
Instead, override GetSerializableMembers()
and CreateProperty()
.相反,重写
GetSerializableMembers()
和CreateProperty()
。
You need to filter out indexed properties as these cannot be serialized.您需要过滤掉索引属性,因为这些属性无法序列化。
You probably want to use BindingFlags.FlattenHierarchy
which Specifies that public and protected static members up the hierarchy should be returned.您可能希望使用
BindingFlags.FlattenHierarchy
指定应返回层次结构上的公共和受保护 static 成员。
I renamed your class from CustomJsonSerializerSettings
to CustomJsonContractResolver
because it is a custom contract resolver which is passed into settings , and not setting itself.我将您的 class 从
CustomJsonSerializerSettings
重命名为CustomJsonContractResolver
因为它是一个自定义合同解析器,它被传递到settings 中,而不是设置自己。
Newtonsoft recommends to statically cache and reuse contract resolvers for best performance. Newtonsoft 建议静态缓存和重用合约解析器以获得最佳性能。
For more on secret backing fields see Is it possible to access backing fields behind auto-implemented properties?有关秘密支持字段的更多信息,请参阅是否可以访问自动实现属性后面的支持字段? .
.
This contract resolver may not work correctly for opt-in contracts such as data contract types .此合同解析程序可能无法正常用于数据合同类型等选择加入合同。 If any of your types have opt-in contracts you may need to specify how these should be serialized and test to see whether you are getting the desired results.
如果您的任何类型都有选择加入的合同,您可能需要指定如何序列化这些合同并进行测试以查看您是否获得了所需的结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.