缩小序列化的JSON的大小
在把.NET对象序列化为JSON时可能的一个常见的问题是,JSON最终包含了很多不想要的属性和值。在把JSON返回到客户端时,它可能会特别显著。更大的JSON意味着占用更大的带宽和产生更慢的网站。
为了解决不想要的JSON的问题,Json.NET有很多的内置选项,来细调序列化对象中会写入哪些内容。
JsonIgnoreAttribute和DataMemberAttribute
默认情况下,Json.NET将在它创建的JSON中包含一个类的所有公开的属性和字段。把JsonIgnoreAttribute添加到属性上,会告诉序列化器始终跳过把此属性写入JSON结果。
Opt-out序列化示例
public class Car
{
// included in JSON
public string Model { get; set; }
public DateTime Year { get; set; }
public List<string> Features { get; set; }
// ignored
[JsonIgnore]
public DateTime LastModified { get; set; }
}
如果一个类具有很多属性,你可能只想序列化它的一个小小的子集,则对所有的其它属性添加JsonIgnore会很麻烦,容易出错。解决这种情况的办法是给类添加DataContractAttribute,给要序列化的属性添加DataMemberAttribute。这是opt-in序列化——只有你标记了的属性才会被序列化,而不像opt-out序列化那样使用JsonIgnoreAttrubute。
Opt-in序列化示例
[DataContract]
public class Computer
{
// included in JSON
[DataMember]
public string Name { get; set; }
[DataMember]
public decimal SalePrice { get; set; }
// ignored
public string Manufacture { get; set; }
public int StockCount { get; set; }
public decimal WholeSalePrice { get; set; }
public DateTime NextShipmentDate { get; set; }
}
格式化
序列化器编写的JSON,如果其选项Formatting设置为Indented,会产生格式化良好的、容易阅读的JSON,在开发时非常有利于可读性。另一方面,Formatting.None会使JSON结果尽可能小,跳过所有不必要的空格和分行,以产生更紧凑的高效的JSON。
空值处理
NullValueHandling是JsonSerializer上的一个选项,控制了序列化器如何处理带有空值的属性。如果设置了值NullValueHandling.Ignore,则JsonSerializer将跳过写入任何带有空值的属性。
NullValueHandling类
public class Movie
{
public string Name { get; set; }
public string Description { get; set; }
public string Classification { get; set; }
public string Studio { get; set; }
public DateTime? ReleaseDate { get; set; }
public List<string> ReleaseCountries { get; set; }
}
NullValueHandling忽略示例
Movie movie = new Movie();
movie.Name = "Bad Boys III";
movie.Description = "It's no Bad Boys";
string included = JsonConvert.SerializeObject(movie,
Formatting.Indented,
new JsonSerializerSettings { });
// {
// "Name": "Bad Boys III",
// "Description": "It's no Bad Boys",
// "Classification": null,
// "Studio": null,
// "ReleaseDate": null,
// "ReleaseCountries": null
// }
string ignored = JsonConvert.SerializeObject(movie,
Formatting.Indented,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
// {
// "Name": "Bad Boys III",
// "Description": "It's no Bad Boys"
// }
还可以使用JsonPropertyAttribute,在单个属性上自定义NullValueHandling。JsonPropertyAttribute的值NullValueHandling将针对此属性覆盖JsonSerializer上的设置。
默认值处理
DefaultValueHandling是JsonSerializer上的一个选项,控制了序列化器处理带有默认值的属性的方式。设置值DefaultValueHandling.Ignore将使JsonSerializer跳过把任何带有默认值的属性写入到JSON结果中。对于对象引用,默认值是null。对于值类型,像int和DateTime,序列化器将针对这些值类型,跳过默认的未初始化的值。
Json.NET还允许你利用DefaultValueAttribute来自定义单个属性的默认值是什么。例如,如果一个称为Department的字符串属性在它的默认状态中始终返回一个空字符串,而且你不想要在JSON中有空字符串,则在Department上放置DefaultValueAttribute,带上那个值,意味着Department将不再会写入到JSON中,除非它具有值。
DefaultValueHandling示例
public class Invoice
{
public string Company { get; set; }
public decimal Amount { get; set; }
// false is default value of bool
public bool Paid { get; set; }
// null is default value of nullable
public DateTime? PaidDate { get; set; }
// customize default values
[DefaultValue(30)]
public int FollowUpDays { get; set; }
[DefaultValue("")]
public string FollowUpEmailAddress { get; set; }
}
DefaultValueHandling忽略示例
Invoice invoice = new Invoice
{
Company = "Acme Ltd.",
Amount = 50.0m,
Paid = false,
FollowUpDays = 30,
FollowUpEmailAddress = string.Empty,
PaidDate = null
};
string included = JsonConvert.SerializeObject(invoice,
Formatting.Indented,
new JsonSerializerSettings { });
// {
// "Company": "Acme Ltd.",
// "Amount": 50.0,
// "Paid": false,
// "PaidDate": null,
// "FollowUpDays": 30,
// "FollowUpEmailAddress": ""
// }
string ignored = JsonConvert.SerializeObject(invoice,
Formatting.Indented,
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
// {
// "Company": "Acme Ltd.",
// "Amount": 50.0
// }
可以在单个属性上利用JsonPropertyAttribute自定义DefaultValueHandling。JsonPropertyAttribute的值DefaultValueHandling将针对此属性覆盖JsonSerializer上的设置。
IContractResolver
为了更大的灵活性,IContractResolver提供了一个接口,来自定义关于.NET对象如何序列化为JSON的几乎所有的方面,包括在运行时改变序列化行为。
IContractResolver示例
public class DynamicContractResolver : DefaultContractResolver
{
private readonly char _startingWithChar;
public DynamicContractResolver(char startingWithChar)
{
_startingWithChar = startingWithChar;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
// only serializer properties that start with the specified character
properties =
properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList();
return properties;
}
}
public class Book
{
public string BookName { get; set; }
public decimal BookPrice { get; set; }
public string AuthorName { get; set; }
public int AuthorAge { get; set; }
public string AuthorCountry { get; set; }
}
IContractResolver示例
Book book = new Book
{
BookName = "The Gathering Storm",
BookPrice = 16.19m,
AuthorName = "Brandon Sanderson",
AuthorAge = 34,
AuthorCountry = "United States of America"
};
string startingWithA = JsonConvert.SerializeObject(book, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('A') });
// {
// "AuthorName": "Brandon Sanderson",
// "AuthorAge": 34,
// "AuthorCountry": "United States of America"
// }
string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') });
// {
// "BookName": "The Gathering Storm",
// "BookPrice": 16.19
// }