我想序列化一组第三方结构,但它们有很多不需要序列化的属性。我想从序列化中排除非自动属性(因为所有这些属性都不是自动的)。
我如何通过自定义合同解析器或其他方式做到这一点?同时,我想包括私有字段的序列化。
实现这一点的正确方法是什么?
您可以通过编写自定义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
名称的 Auto 属性创建一个 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
类的自定义 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 + ">"));
}
}