【问题标题】:C# Serialize with JSON.NET inherited private fields使用 JSON.NET 继承的私有字段进行 C# 序列化
【发布时间】:2016-12-15 08:30:06
【问题描述】:

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

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}}”。然后反序列化

【问题讨论】:

  • 那么您要么必须创建自定义序列化程序,要么将字段设为非私有。请注意,有时最好的解决方案不是序列化程序使用的类,而是创建具有序列化所需结构的单独的序列化友好类,然后来回映射。这是为了避免出现您现在遇到的此类问题,或者对您正在使用的类进行序列化调整。

标签: c# json.net


【解决方案1】:

假设您的对象结构(在外部 dll 中) 不能以任何方式修改,您仍然可以使用内部使用 custom ContractResolvercustom JsonConverter 创建所需的 JSON使用相关的 get 和 set 方法在类型层次结构的每个级别生成公共和私有字段的列表:

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);
    }
}

然后按如下方式使用:

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

示例fiddle

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 2020-10-25
    • 1970-01-01
    • 2014-07-29
    • 2013-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多