从 Json.Net 中的序列化中排除非自动属性

本文关键字:属性 排除 序列化 Json Net | 更新日期: 2023-09-27 18:33:51

我想序列化一组第三方结构,但它们有很多不需要序列化的属性。我想从序列化中排除非自动属性(因为所有这些属性都不是自动的)。

我如何通过自定义合同解析器或其他方式做到这一点?同时,我想包括私有字段的序列化。

实现这一点的正确方法是什么?

从 Json.Net 中的序列化中排除非自动属性

您可以通过编写自定义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 + ">"));
    }
}