Newtonsoft Json.NET动态决定属性是否序列化
今天遇到一个需求,将一个类序列化时,需要动态决定哪些属性是否执行序列化。Json.NET作为一款强大的json序列化工具,提供ContractResolver以实现神乎奇技的高度动态化
下面这个范例,展示两种动态决定应序列化属性的情境:
JSerialize时传入属性名称数组作为参数,指定JSON应包含的属性。由对象属性值决定属性是否要序列化,例如: 如果是女生就不包含年龄。(这几乎已弹性到极点,虽然实务上不常用到)
程序的做法是提供两个继承自DefaultContractResolver的类:
-
LimitPropsContractResolver在建构时传入string[]参数列出要序列化的属性名称,并覆写CreateProperties方法,过滤base.CreateProperties()传回的IList
,只保留前述string[]有列出的属性; -
HideAgeContractResolver则覆写CreateProperty()方法,由base.CreateProperty()取得JsonProperty,JsonProperty有个ShouldSerialize属性可以传入Lambda表达式,遍历处理每个要序列化的对象,在Lambda表达式中可将对象转型为原型别进行判断,若不要序列化就返回false
下面是代码演示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
namespace ConsoleApplication1
{
class Program
{
public enum Gender
{
Male, Female
}
public class Person
{
public string Name { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public Gender Gender { get; set; }
public int Age { get; set; }
public Person(string name, Gender gender, int age)
{
Name = name; Gender = gender; Age = age;
}
}
public class HideAgeContractResolver : DefaultContractResolver
{
//REF: http://james.newtonking.com/projects/json/help/index.html?topic=html/ContractResolver.htm
protected override JsonProperty CreateProperty(MemberInfo member,
MemberSerialization memberSerialization)
{
JsonProperty p = base.CreateProperty(member, memberSerialization);
if (p.PropertyName == "Age")
{
//依性别决定是否要序列化
p.ShouldSerialize = instance =>
{
Person person = (Person)instance;
return person.Gender == Gender.Male;
};
}
return p;
}
}
public class LimitPropsContractResolver : DefaultContractResolver
{
string[] props = null;
bool retain;
public LimitPropsContractResolver(string[] props,bool retain=true)
{
//指定要序列化属性的清单
this.props = props;
this.retain = retain;
}
//REF: http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size.aspx
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> list = base.CreateProperties(type, memberSerialization);
//只保留清单有列出的属性
return list.Where(p =>
{
if (retain)
{
return props.Contains(p.PropertyName);
}
else
{
return !props.Contains(p.PropertyName);
}
}).ToList();
}
}
static void Main(string[] args)
{
List<Person> list = new List<Person>();
list.Add(new Person("George", Gender.Male, 18));
list.Add(new Person("Mary", Gender.Female, 40));
//正常输出
Console.WriteLine(JsonConvert.SerializeObject(
list, Formatting.Indented));
var settings = new JsonSerializerSettings();
//加上ContractResolver,正向表列哪些属性要序列化
settings.ContractResolver =
new LimitPropsContractResolver("Name,Age".Split(','));
Console.WriteLine(JsonConvert.SerializeObject(
list, Formatting.Indented, settings));
//加上ContractResolver,依对象的属性值动态决定要不要序列化
settings.ContractResolver = new HideAgeContractResolver();
Console.WriteLine(JsonConvert.SerializeObject(
list, Formatting.Indented, settings));
Console.ReadLine();
}
}
}
程序执行结果如下,共有三段输出,第一段为正常版;第二段套用LimitPropsContractResolver(“Name,Age”.Split(‘,’)),故JSON中只见Name及Age,Gender被隐藏;第三段套用了HideAgeContractResolver(),如结果所示,Mary的JSON内容不包含年龄,George则包含。
[
{
"Name": "George",
"Gender": "Male",
"Age": 18
},
{
"Name": "Mary",
"Gender": "Female",
"Age": 40
}
]
[
{
"Name": "George",
"Age": 18
},
{
"Name": "Mary",
"Age": 40
}
]
[
{
"Name": "George",
"Gender": "Male",
"Age": 18
},
{
"Name": "Mary",
"Gender": "Female"
}
]