从Json.Net中的序列化中排除非autoproperties

时间:2014-12-10 08:08:20

标签: c# serialization json.net

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

我怎么能通过CustomContractResolver或其他方式做到这一点? 同时我想包括私有字段的序列化。

实现此目的的正确方法是什么?

1 个答案:

答案 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 + ">"));
    }
}