简体   繁体   中英

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. 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:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM