I have implemented a JsonSerializer with a "custom" ContractResolver which overrides the binding-flags so I can actually serialize private and protected members. 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. However when there are protected attrs from the parent-class are involved, it generates some strange extra information in JSON-string called "k__Backingfield".
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:
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.
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. 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 .
You should only set JsonProperty.Readable
and JsonProperty.Writable
if the corresponding get- and set- methods exist. 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.
Instead, override GetSerializableMembers()
and 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.
I renamed your class from CustomJsonSerializerSettings
to CustomJsonContractResolver
because it is a custom contract resolver which is passed into settings , and not setting itself.
Newtonsoft recommends to statically cache and reuse contract resolvers for best performance.
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.
Demo fiddle here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.