基于属性属性

时间:2015-10-20 13:01:09

标签: c# json serialization json.net

我正在寻找一种使用Json.Net将模型部分序列化为Json的方法。部分序列化应该如何看起来像我想要在父对象的属性上定义。因此,部分序列化对于不同的父模型可能看起来不同。为了说明我想要的一些代码:

private class MyTestObject
{
    [SerializeOnly("TestValue1")]
    [SerializeOnly("TestValue3")]
    public ComplexTestObject Property1 { get; set; }

    public MyTestObject()
    {
       Property1 = new ComplexTestObject();
    }
}

private class ComplexTestObject
{
    public string TestValue1 { get; set; }
    public string TestValue2 { get; set; }
    public string TestValue3 { get; set; }
    public ComplexTestObject()
    {
        TestValue1 = "value1";
        TestValue2 = "value2";
        TestValue3 = "value3";
    }
}

现在当我序列化一个类MyTestObject的实例时,我希望获得以下Json:

{
    "Property1" : {
        "TestValue1" : "value1",
        "TestValue3" : "value3",
    }
}

您可以看到SerializeOnly用于定义要序列化的属性。

为实现这一目标,我可以创建一个SerializeOnlyAttribute。当尝试在自定义Serialization ContractResolver中使用它时,我只能看到特定成员的属性,因此我看不到任何SerializeOnlyAttribute,因为它们位于父级。

有没有一种简单的方法来实现Json.Net所需的行为?可能有可能编写一个自定义的JsonConverter但是如何构建它以便只覆盖处理属性并且仍然使用默认的转换器?

2 个答案:

答案 0 :(得分:1)

您可以分两部分来解决这个问题:

  • 创建一个自定义JsonConverter,它可以接受要序列化的属性名称列表。
  • 创建一个自定义ContractResolver,用于查找应用了至少一个[SerializeOnly]属性的属性,并将自定义转换器应用于这些属性,并传递从应用属性收集的子属性名称列表。

以下是解析器的外观:

class CustomResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
        foreach (JsonProperty prop in props)
        {
            if (!prop.PropertyType.IsPrimitive && prop.PropertyType != typeof(string))
            {
                PropertyInfo pi = type.GetProperty(prop.UnderlyingName);
                if (pi != null && pi.CanRead)
                {
                    var childPropertiesToSerialize = pi.GetCustomAttributes<SerializeOnly>()
                                                       .Select(att => att.PropertyName);
                    if (childPropertiesToSerialize.Any())
                    {
                        prop.Converter = new CustomConverter(childPropertiesToSerialize);
                    }
                }
            }
        }
        return props;
    }
}

这是转换器:

class CustomConverter : JsonConverter
{
    private HashSet<string> propertiesToSerialize;

    public CustomConverter(IEnumerable<string> propertiesToSerialize)
    {
        this.propertiesToSerialize = new HashSet<string>(propertiesToSerialize);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        foreach (PropertyInfo prop in value.GetType().GetProperties())
        {
            if (prop.CanRead && propertiesToSerialize.Contains(prop.Name))
            {
                writer.WritePropertyName(prop.Name);
                serializer.Serialize(writer, prop.GetValue(value));
            }
        }
        writer.WriteEndObject();
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }
}

演示:

class Program
{
    static void Main(string[] args)
    {
        var test = new MyTestObject();
        var settings = new JsonSerializerSettings();
        settings.ContractResolver = new CustomResolver();
        settings.Formatting = Formatting.Indented;
        var json = JsonConvert.SerializeObject(test, settings);
        Console.WriteLine(json);
    }

    class MyTestObject
    {
        [SerializeOnly("TestValue1")]
        [SerializeOnly("TestValue3")]
        public ComplexTestObject Property1 { get; set; }

        [SerializeOnly("TestValue2")]
        public ComplexTestObject Property2 { get; set; }

        public MyTestObject()
        {
            Property1 = new ComplexTestObject();
            Property2 = new ComplexTestObject();
        }
    }

    class ComplexTestObject
    {
        public string TestValue1 { get; set; }
        public string TestValue2 { get; set; }
        public string TestValue3 { get; set; }
        public ComplexTestObject()
        {
            TestValue1 = "value1";
            TestValue2 = "value2";
            TestValue3 = "value3";
        }
    }
}

输出:

{
  "Property1": {
    "TestValue1": "value1",
    "TestValue3": "value3"
  },
  "Property2": {
    "TestValue2": "value2"
  }
}

小提琴:https://dotnetfiddle.net/Fj7QcW

答案 1 :(得分:-1)

C#已经有一个属性可以帮助你解决这个问题

https://msdn.microsoft.com/en-us/library/system.web.script.serialization.scriptignoreattribute(v=vs.110).aspx

[ScriptIgnore]
public string MyParam{get;set;}