C#使用JSON.NET继承的私有字段进行序列化 [英] C# Serialize with JSON.NET inherited private fields

查看:138
本文介绍了C#使用JSON.NET继承的私有字段进行序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个这样的对象结构(在外部dll中):

I have an object structure (in external dll) like this:

public class Demo2 { 
    private int count;
    public Demo2() { 
        count = 2;
    }
}
public class MyDemo : Demo2  {
    private int count;
    public MyDemo() { 
        count = 3;
    }
}
public class Perform    {
    static void Main(string[] args)    {
        MyDemo d = new MyDemo();    
        String json = JsonSerializer.SerializeOnce<MyDemo>(d);
        Console.WriteLine(json);
        /* print: {count: 3} */
    }
}

我需要以下内容:"{count:3,base:{count:2}}".然后反序列化

I need something this: "{count: 3, base: {count:2}}". And Deserialize later

推荐答案

假定您的对象结构(在外部dll中)不能以任何方式进行修改,您仍然可以使用来创建所需的JSON在内部使用自定义JsonConverter ="http://www.newtonsoft.com/json/help/html/contractresolver.htm" rel ="nofollow noreferrer">自定义ContractResolver 来生成每个级别的公共和私有字段的列表具有关联的get和set方法的类型层次结构:

Assuming your object structure (in external dll) cannot be modified in any way, you can still create the JSON you require by using a custom JsonConverter that internally makes use of a custom ContractResolver to generate a list of public and private fields at each level of the type hierarchy with associated get and set methods:

public class DeclaredFieldJsonConverter<T> : JsonConverter where T: new()
{
    const string basePropertyName = "base";

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        var jObj = JObject.Load(reader);

        existingValue = existingValue ?? new T();
        var type = existingValue.GetType();

        while (jObj != null && type != null)
        {
            var basejObj = jObj.ExtractPropertyValue(basePropertyName) as JObject;
            JsonObjectContract contract = (JsonObjectContract)DeclaredFieldContractResolver.Instance.ResolveContract(type);
            foreach (var jProperty in jObj.Properties())
            {
                var property = contract.Properties.GetClosestMatchProperty(jProperty.Name);
                if (property == null)
                    continue;
                var value = jProperty.Value.ToObject(property.PropertyType, serializer);
                property.ValueProvider.SetValue(existingValue, value);
            }
            type = type.BaseType;
            jObj = basejObj;
        }

        return existingValue;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        WriteJson(writer, value, value.GetType(), serializer);
    }

    void WriteJson(JsonWriter writer, object value, Type type, JsonSerializer serializer)
    {
        JsonObjectContract contract = (JsonObjectContract)DeclaredFieldContractResolver.Instance.ResolveContract(type);

        writer.WriteStartObject();
        foreach (var property in contract.Properties.Where(p => !p.Ignored))
        {
            writer.WritePropertyName(property.PropertyName);
            serializer.Serialize(writer, property.ValueProvider.GetValue(value));
        }

        var baseType = type.BaseType;
        if (baseType != null && baseType != typeof(object))
        {
            writer.WritePropertyName(basePropertyName);
            WriteJson(writer, value, baseType, serializer);
        }

        writer.WriteEndObject();
    }
}

public static class JsonExtensions
{
    public static JToken ExtractPropertyValue(this JObject obj, string name)
    {
        if (obj == null)
            return null;
        var property = obj.Property(name);
        if (property == null)
            return null;
        var value = property.Value;
        property.Remove();
        property.Value = null;
        return value;
    }
}

class DeclaredFieldContractResolver : DefaultContractResolver
{
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
    static DeclaredFieldContractResolver instance;

    // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
    static DeclaredFieldContractResolver() { instance = new DeclaredFieldContractResolver(); }

    public static DeclaredFieldContractResolver Instance { get { return instance; } }

    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        var fields = objectType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(f => !f.IsNotSerialized);
        return fields.Cast<MemberInfo>().ToList();
    }

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);
        contract.MemberSerialization = MemberSerialization.Fields;
        return contract;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return base.CreateProperties(type, MemberSerialization.Fields);
    }
}

然后按如下所示使用它:

Then use it as follows:

var demo = new MyDemo();
var json = JsonConvert.SerializeObject(demo, new DeclaredFieldJsonConverter<MyDemo>());

示例小提琴.

请注意,如果类型层次结构中任何地方都有名为base的字段,则将写入重复的JSON属性名称,从而在反序列化时可能会丢失信息.您可能需要检查并以某种方式处理.

Note that, if there is a field named base anywhere in the type hierarchy, duplicated JSON property names will be written resulting in potential loss of information when deserializing. You may want to check for this and handle it in some manner.

这篇关于C#使用JSON.NET继承的私有字段进行序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆