我想序列化一组第三方结构,但它们有许多我不需要序列化的属性。我想从序列化中排除非autoproperties(因为所有属性都不是自动的)。
我怎么能通过CustomContractResolver或其他方式做到这一点? 同时我想包括私有字段的序列化。
实现此目的的正确方法是什么?
答案 0 :(得分:2)
您可以通过编写自定义JsonConverter
来实现此目的。这是一个例子(我尝试尽可能地编写自定义序列化程序,因此它应该适用于没有或微妙更改的类型):
自定义类型和用法:
[JsonConverter(typeof (CustomSerializer))]
public struct CustomStruct
{
public int PublicInt;
private int _privateInt;
public string PublicString;
private string _privateString;
public int AutoInt { get; set; }
public string AutoString { get; set; }
public int ManualInt
{
get{return _privateInt;}
set { _privateInt = value; }
}
public string ManualString
{
get { return _privateString; }
set { _privateString = value; }
}
}
class Program
{
private static void Main(string[] args)
{
var obj = new CustomStruct()
{
AutoInt = 10,
AutoString = "autostring",
ManualInt = 5,
ManualString = "manualstring",
PublicInt = 20,
PublicString = "publicstring"
};
var json = JsonConvert.SerializeObject(obj,Formatting.Indented);
var dObj = JsonConvert.DeserializeObject<CustomStruct>(json);
}
}
我们希望我们的json结果类似于
{
"AutoInt": 10,
"AutoString": "autostring",
"PublicInt": 20,
"_privateInt": 5,
"PublicString": "publicstring",
"_privateString": "manualstring"
}
工作原理:
C#编译器将为每个具有模式<PropertName>k__BackingField
的名称的自动属性创建一个Back字段,所以基本上我们可以利用这种模式并找到它们。(注意:此代码段可能不适用于Mono,或者未来.Net编译器)
public static bool IsAutoProperty(PropertyInfo prop)
{
return prop.DeclaringType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Any(f => f.Name.Contains("<" + prop.Name + ">"));
}
下一步很简单,我们应该基于JsonConverter
类实现一个Custom Json序列化程序。
自定义序列化程序:
public class CustomSerializer : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof (CustomSerializer) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var value = existingValue ?? Activator.CreateInstance(objectType);
PopulateAutoProperties(objectType, jObject, value);
PopulateFields(objectType, jObject, value);
return value;
}
private static void PopulateAutoProperties(Type objectType, JObject jObject, object value)
{
var properties =
objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var p in properties.Where(IsAutoProperty))
{
var token = jObject[p.Name];
var obj = token != null
? token.ToObject(p.PropertyType)
: p.PropertyType.IsValueType ? Activator.CreateInstance(p.PropertyType) : null;
p.SetValue(value, obj);
}
}
private static void PopulateFields(Type objectType, JObject jObject, object value)
{
var fields =
objectType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var f in fields.Where(f => !f.Name.Contains("<")))
{
var token = jObject[f.Name];
var obj = token != null
? token.ToObject(f.FieldType)
: f.FieldType.IsValueType ? Activator.CreateInstance(f.FieldType) : null;
f.SetValue(value, obj);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var objectType=value.GetType();
writer.WriteStartObject();
WriteAutoProperties(writer, value, serializer, objectType);
WriteFields(writer, value, serializer, objectType);
writer.WriteEndObject();
}
private static void WriteFields(JsonWriter writer, object value, JsonSerializer serializer, Type objectType)
{
var fields = objectType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var f in fields.Where(f => !f.Name.Contains("<")))
{
writer.WritePropertyName(f.Name);
serializer.Serialize(writer, f.GetValue(value));
}
}
private static void WriteAutoProperties(JsonWriter writer, object value, JsonSerializer serializer, Type objectType)
{
var properties =
objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var p in properties.Where(IsAutoProperty))
{
writer.WritePropertyName(p.Name);
serializer.Serialize(writer, p.GetValue(value));
}
}
public static bool IsAutoProperty(PropertyInfo prop)
{
return prop.DeclaringType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Any(f => f.Name.Contains("<" + prop.Name + ">"));
}
}