当序列化.Net对象为Json对象时经常会遇到的一个问题是:最终的Json包含大量多余的属性和属性值。这个问题在返回Json到客户端时显得特别重要。Json越大意味着需要更大的带宽,使网速变得更慢。
为了解决多余的Json这个问题,Json.Net有一系列内置的选项可以进行调整。
->JsonIgnoreAttribute and DataMemberAttribute
默认情况下,在Json创建的时候Json.Net会包含所有类级别的public属性和字段。添加JsonIgnoreAttribute到属性上,告诉序列化器序列化时跳过它。
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; }
}
如果类有很多属性,你只想序列化它的一小部分,添加JsonIgore到所有其他的属性上会比较冗余,也比较容易出错。有一种用来处理这种情况的方法,添加DataContractAttribute到类上,添加DataMemberAttribute到需要被序列化的属性上。与使用JsonIgnoreAttribute的opt-out序列化相比,opt-in序列化仅仅你标记的属性被需列化。
using System.Runtime.Serialization;
[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; }
}
->Formatting
Json序列化时,用可选参数Formatting.Indented生成良好的显示格式,可读性更好。另一方面,Formatting.None会跳过不必要的空格和换行符,让Json的结果更小。生成的显示格式更加紧凑,也许效率更高。
->NullValueHandling
在序列化器中NullVlaueHandling是可选的。它控制序列化器如何处理值为null的属性。通过设置NullValueHandling.Ignore值,序列化器会跳过值为null的属性。
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; }
}
测试1:
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 { });
Console.WriteLine(included);
输出结果:
{
"Name": "Bad Boys III",
"Description": "It's no Bad Boys",
"Classification": null,
"Studio": null,
"ReleaseDate": null,
"ReleaseCountries": null
}
测试2:
string ignored = JsonConvert.SerializeObject(movie,
Formatting.Indented,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
Console.WriteLine(ignored);
输出结果:
{
"Name": "Bad Boys III",
"Description": "It's no Bad Boys"
}
NullValueHandling也可以在单独的属性上用JsonPropertyAttribute进行定制。使用JsonPropertyAttribute时设置的NullValueHandling值会在Json序列化器中为该属性进行重写。
->DefaultValuehandling
在序列化器中DefaultValuehandling是可选的。它控制序列化器如何处理带有默认值的属性。通过设置DefaultValuehandling.Ignore值,序列化器会跳过带有默认值的属性。对于引用类型为null,对于值类型,如int和DateTime,序列化器将跳过默认未初使化值的值类型。
Json.Net也允许你通过DefaultValueAttribute自定义默认值,例如:如果一个字符串类型的属性Department在它的默认状态下总是返回一个空字符,但是在你的Json对象中你不想要那个空字符串,你可以在Department上使用空字符串参数标记DefaultValueAttribute,这意味着Department在序列化时不再写入Json中,除非它有非空值。
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; }
}
测试1:
Invoice invoice = new Invoice
{
Company = "Acme Ltd.",
Amount = 50.0m,
Paid = false,
FollowUpDays = 30,
FollowUpEmailAddress = string.Empty,
PaidDate = null
};
string includedDefaultValue = JsonConvert.SerializeObject(invoice,
Formatting.Indented,
new JsonSerializerSettings { });
Console.WriteLine(includedDefaultValue);
输出结果:
{
"Company": "Acme Ltd.",
"Amount": 50.0,
"Paid": false,
"PaidDate": null,
"FollowUpDays": 30,
"FollowUpEmailAddress": ""
}
测试2:
string ignoredDefaultValue = JsonConvert.SerializeObject(invoice,
Formatting.Indented,
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
Console.WriteLine(ignoredDefaultValue);
输出结果:
{
"Company": "Acme Ltd.",
"Amount": 50.0,
"Paid": false//??
}
DefaultValueHandling也可以在单独的属性上用JsonPropertyAttribute进行定制。使用JsonPropertyAttribute时设置的DefaultValueHandling值会在Json序列化器中为该属性进行重写。
->IContractResolver
为了获得更多的灵活性,IContractResolver接口提供了定制.Net对象序列化为Json的每一个方面,包括在运行时改变序列化的行为。
public class DynamicContractResolver : DefaultContractResolver
{
private readonly char _startingWithChar;
public DynamicContractResolver(char startingWithChar)
{
_startingWithChar = startingWithChar;
}
protected override IList<JsonProperty> CreateProperties(JsonObjectContract contract)
{
IList<JsonProperty> properties = base.CreateProperties(contract);
// 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; }
}
测试:
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') });
Console.WriteLine(startingWithA);
// {
// "AuthorName": "Brandon Sanderson",
// "AuthorAge": 34,
// "AuthorCountry": "United States of America"
// }
string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') });
Console.WriteLine(startingWithA);
// {
// "BookName": "The Gathering Storm",
// "BookPrice": 16.19
// }