c#使用JSON.NET继承私有字段进行序列化
我有一个像这样的对象结构(在外部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();我需要这个:“{count:3,base:{count:2}}”。 稍后反序列化
String json = JsonSerializer.SerializeOnce(d); Console.WriteLine(json); /* print: {count: 3} */ } }
假设你的对象结构(在外部DLL中)不能以任何方式修改,你仍然可以通过使用自定义的JsonConverter
来创建你需要的JSON,这个JsonConverter
在内部使用一个定制的ContractResolver
来生成每个级别的公共和专用字段列表具有关联的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>());
样品小提琴。
请注意,如果在类型层次结构中存在名为base
的字段,则会写入重复的JSON属性名称,导致反序列化时可能会丢失信息。 您可能需要检查并以某种方式处理它。
上一篇: c# Serialize with JSON.NET inherited private fields
下一篇: how to delete selected row in datagrid using delete key with fixed column