以下为json.net的一系列参数设置以及属性设置说明,翻译自官方文档。有错误的地方请指正。接前篇。
在JSON中序列化日期
JSON中的DateTimes很难使用。
问题来自JSON规范本身:JSON中没有日期的文字语法。 规范包含对象,数组,字符串,整数和浮点数,但它没有定义日期的标准。
Json.NET使用的默认格式是ISO 8601标准:“2012-03-19T07:22Z”。
在Json.NET 4.5之前,日期是使用Microsoft格式编写的:“\ / Date(1198908717056)\ /”。 如果要使用此格式,或者希望保持与Microsoft JSON序列化程序或旧版本Json.NET的兼容性,请将DateFormatHandling设置更改为MicrosoftDateFormat。
DateTimeZoneHandling设置可用于在序列化时转换DateTime的DateTimeKind。 例如,将DateTimeZoneHandling设置为Utc以将所有DateTime序列化为UTC日期。 请注意,此设置不会影响DateTimeOffsets。
如果您的日期不符合ISO 8601标准,则DateFormatString设置可用于自定义使用.NET的自定义日期和时间格式语法读取和写入的日期字符串的格式。
由于JSON中的日期没有标准,因此在与其他系统交互时可能存在的不同格式的数量是无穷无尽的。 幸运的是,Json.NET有一个处理读写自定义日期的解决方案:JsonConverters。 JsonConverter用于覆盖类型的序列化方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class LogEntry
{
public string Details { get; set; }
public DateTime LogDate { get; set; }
}
[Test]
public void WriteJsonDates()
{
LogEntry entry = new LogEntry
{
LogDate = new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc),
Details = "Application started."
};
// default as of Json.NET 4.5
string isoJson = JsonConvert.SerializeObject(entry);
// {"Details":"Application started.","LogDate":"2009-02-15T00:00:00Z"}
JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
string microsoftJson = JsonConvert.SerializeObject(entry, microsoftDateFormatSettings);
// {"Details":"Application started.","LogDate":"\/Date(1234656000000)\/"}
string javascriptJson = JsonConvert.SerializeObject(entry, new JavaScriptDateTimeConverter());
// {"Details":"Application started.","LogDate":new Date(1234656000000)}
}
|
只需将您希望使用的JsonConverter传递给Json.NET序列化程序即可。
JavaScriptDateTimeConverter类是Json.NET附带的两个DateTime JsonConverters之一。 此转换器将DateTime序列化为JavaScript Date对象:new Date(1234656000000)。
从技术上讲,这是根据规范无效的JSON,但所有浏览器和一些JSON框架,包括Json.NET,都支持它。
IsoDateTimeConverter将DateTime序列化为ISO 8601格式的字符串:“2009-02-15T00:00:00Z”
IsoDateTimeConverter类有一个属性DateTimeFormat,用于进一步自定义格式化字符串。
减少序列化的JSON大小
将.NET对象序列化为JSON时遇到的一个常见问题是JSON最终包含许多不需要的属性和值。将JSON返回给客户端时,这尤其重要。 更多JSON意味着更多带宽和更慢的网站。
为了解决不需要的JSON问题,Json.NET提供了一系列内置选项来微调从序列化对象写入的内容。
默认情况下,Json.NET将在其创建的JSON中包含所有类的公共属性和字段。 将JsonIgnoreAttribute添加到属性会告诉序列化程序始终跳过将其写入JSON结果。
1
2
3
4
5
6
7
8
9
10
11
|
public class Car
{
// included in JSON
public string Model { get; set; }
public DateTime Year { get; set; }
public List
// ignored
[JsonIgnore]
public DateTime LastModified { get; set; }
}
|
如果一个类有许多属性而你只想序列化它们的一小部分,那么将JsonIgnore添加到所有其他属性将是乏味且容易出错的。 解决此方案的方法是将DataContractAttribute添加到类,将DataMemberAttribute添加到要序列化的属性。 这是选择性序列化 - 只有标记的属性才会被序列化,这与使用JsonIgnoreAttribute的选择退出序列化不同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[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,其格式设置为Indented,可生成格式良好,易于阅读的JSON,在开发时非常易于阅读。 另一方面,Formatting.None使JSON结果保持较小,跳过所有不必要的空格和换行符以产生最紧凑和最有效的JSON。
NullValueHandling是JsonSerializer上的一个选项,它控制序列化程序如何使用null值处理属性。 通过设置NullValueHandling.Ignore的值,JsonSerializer将跳过写入任何值为null的属性。
1
2
3
4
5
6
7
8
9
|
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
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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"
// }
|
NullValueHandling也可以使用JsonPropertyAttribute在各个属性上进行自定义。 NullValueHandling的JsonPropertyAttribute值将覆盖该属性的JsonSerializer上的设置。
DefaultValueHandling是JsonSerializer上的一个选项,它控制序列化程序如何使用默认值处理属性。 设置DefaultValueHandling.Ignore的值将使JsonSerializer跳过将具有默认值的任何属性写入JSON结果。 对于对象引用,这将为null。 对于int和DateTime等值类型,序列化程序将跳过该值类型的默认未初始化值。
Json.NET还允许您使用DefaultValueAttribute自定义单个属性的默认值。 例如,如果名为Department的字符串属性始终返回其默认状态的空字符串,并且您不希望JSON中包含该空字符串,则将具有该值的DefaultValueAttribute放在Department上将意味着Department不再写入JSON,除非 它有价值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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。 DefaultValueHandling的JsonPropertyAttribute值将覆盖该属性的JsonSerializer上的设置。
为了获得更大的灵活性,IContractResolver提供了一个界面,可以自定义.NET对象如何序列化为JSON的几乎所有方面,包括在运行时更改序列化行为。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class DynamicContractResolver : DefaultContractResolver
{
private readonly char _startingWithChar;
public DynamicContractResolver(char startingWithChar)
{
_startingWithChar = startingWithChar;
}
protected override IList
{
IList
// 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; }
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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
// }
|
反序列化部分JSON片段
通常在处理大型JSON文档时,您只对一小部分信息感兴趣。 当您想要将该JSON片段反序列化为.NET对象时,这种情况可能很烦人,因为您必须为整个JSON结果定义.NET类。
使用Json.NET,很容易解决这个问题。 使用LINQ to JSON,您可以在将它们传递给Json.NET序列化程序之前提取要反序列化的JSON片段。
1
2
3
4
5
6
|
public class SearchResult
{
public string Title { get; set; }
public string Content { get; set; }
public string Url { get; set; }
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
string googleSearchText = @"{
'responseData': {
'results': [
{
'GsearchResultClass': 'GwebSearch',
'unescapedUrl': 'http://en.wikipedia.org/wiki/Paris_Hilton',
'url': 'http://en.wikipedia.org/wiki/Paris_Hilton',
'visibleUrl': 'en.wikipedia.org',
'cacheUrl': 'http://www.google.com/search?q=cache:TwrPfhd22hYJ:en.wikipedia.org',
'title': 'Paris Hilton - Wikipedia, the free encyclopedia',
'titleNoFormatting': 'Paris Hilton - Wikipedia, the free encyclopedia',
'content': '[1] In 2006, she released her debut album...'
},
{
'GsearchResultClass': 'GwebSearch',
'unescapedUrl': 'http://www.imdb.com/name/nm0385296/',
'url': 'http://www.imdb.com/name/nm0385296/',
'visibleUrl': 'www.imdb.com',
'cacheUrl': 'http://www.google.com/search?q=cache:1i34KkqnsooJ:www.imdb.com',
'title': 'Paris Hilton',
'titleNoFormatting': 'Paris Hilton',
'content': 'Self: Zoolander. Socialite Paris Hilton...'
}
],
'cursor': {
'pages': [
{
'start': '0',
'label': 1
},
{
'start': '4',
'label': 2
},
{
'start': '8',
'label': 3
},
{
'start': '12',
'label': 4
}
],
'estimatedResultCount': '59600000',
'currentPageIndex': 0,
'moreResultsUrl': 'http://www.google.com/search?oe=utf8&ie=utf8...'
}
},
'responseDetails': null,
'responseStatus': 200
}";
JObject googleSearch = JObject.Parse(googleSearchText);
// get JSON result objects into a list
IList
// serialize JSON results into .NET objects
IList
foreach (JToken result in results)
{
// JToken.ToObject is a helper method that uses JsonSerializer internally
SearchResult searchResult = result.ToObject
searchResults.Add(searchResult);
}
// Title = Paris Hilton - Wikipedia, the free encyclopedia
// Content = [1] In 2006, she released her debut album...
// Url = http://en.wikipedia.org/wiki/Paris_Hilton
// Title = Paris Hilton
// Content = Self: Zoolander. Socialite Paris Hilton...
// Url = http://www.imdb.com/name/nm0385296/
|
条件属性序列化
Json.NET能够通过在类上放置ShouldSerialize方法来有条件地序列化属性。 此功能类似于XmlSerializer ShouldSerialize功能。
要有条件地序列化属性,请添加一个返回与该属性同名的布尔值的方法,然后使用ShouldSerialize作为方法名称的前缀。方法的结果确定属性是否已序列化。 如果该方法返回true,则该属性将被序列化,如果它返回false,则将跳过该属性。
1
2
3
4
5
6
7
8
9
10
11
|
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
Employee joe = new Employee();
joe.Name = "Joe Employee";
Employee mike = new Employee();
mike.Name = "Mike Manager";
joe.Manager = mike;
// mike is his own manager
// ShouldSerialize will skip this property
mike.Manager = mike;
string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented);
// [
// {
// "Name": "Joe Employee",
// "Manager": {
// "Name": "Mike Manager"
// }
// },
// {
// "Name": "Mike Manager"
// }
// ]
|
也可以使用IContractResolver设置ShouldSerialize。 如果您不想在类上放置ShouldSerialize方法,或者您没有声明类并且无法使用IContractResolver,则有条件地使用IContractResolver序列化属性非常有用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
|
使用ContractResolver进行序列化
IContractResolver接口提供了一种方法来自定义JsonSerializer如何将.NET对象序列化和反序列化为JSON,而无需在类上放置属性。
还可以使用IContractResolver设置可以使用属性或方法控制序列化在对象,集合,属性等上设置的任何内容。
DefaultContractResolver是序列化程序使用的默认解析程序。 它以虚拟方法的形式提供了许多可扩展的途径,可以被覆盖。
CamelCasePropertyNamesContractResolver继承自DefaultContractResolver,只是覆盖要在camelcase中编写的JSON属性名称。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Product product = new Product
{
ExpiryDate = new DateTime(2010, 12, 20, 18, 1, 0, DateTimeKind.Utc),
Name = "Widget",
Price = 9.99m,
Sizes = new[] { "Small", "Medium", "Large" }
};
string json =
JsonConvert.SerializeObject(
product,
Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }
);
//{
// "name": "Widget",
// "expiryDate": "2010-12-20T18:01:00Z",
// "price": 9.99,
// "sizes": [
// "Small",
// "Medium",
// "Large"
// ]
//}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class ConverterContractResolver : DefaultContractResolver
{
public new static readonly ConverterContractResolver Instance = new ConverterContractResolver();
protected override JsonContract CreateContract(Type objectType)
{
JsonContract contract = base.CreateContract(objectType);
// this will only be called once and then cached
if (objectType == typeof(DateTime) || objectType == typeof(DateTimeOffset))
{
contract.Converter = new JavaScriptDateTimeConverter();
}
return contract;
}
}
|
此示例使用IContractResolver为类型设置JsonConverter。 在这里使用契约解析器很有用,因为DateTime不是您自己的类型,并且无法在其上放置JsonConverterAttribute。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
{
property.ShouldSerialize =
instance =>
{
Employee e = (Employee)instance;
return e.Manager != e;
};
}
return property;
}
}
|
使用序列化跟踪进行调试
Json.NET序列化程序支持使用ITraceWriter接口进行日志记录和调试。 通过分配跟踪编写器,您可以捕获序列化消息和错误,并在序列化和反序列化JSON时调试Json.NET序列化程序中发生的事情。
可以使用JsonSerializerSettings或JsonSerializer上的属性分配跟踪编写器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
Staff staff = new Staff();
staff.Name = "Arnie Admin";
staff.Roles = new List
staff.StartDate = new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc);
ITraceWriter traceWriter = new MemoryTraceWriter();
JsonConvert.SerializeObject(
staff,
new JsonSerializerSettings { TraceWriter = traceWriter, Converters = { new JavaScriptDateTimeConverter() } });
Console.WriteLine(traceWriter);
// 2012-11-11T12:08:42.761 Info Started serializing Newtonsoft.Json.Tests.Serialization.Staff. Path ''.
// 2012-11-11T12:08:42.785 Info Started serializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path 'StartDate'.
// 2012-11-11T12:08:42.791 Info Finished serializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path 'StartDate'.
// 2012-11-11T12:08:42.797 Info Started serializing System.Collections.Generic.List`1[System.String]. Path 'Roles'.
// 2012-11-11T12:08:42.798 Info Finished serializing System.Collections.Generic.List`1[System.String]. Path 'Roles'.
// 2012-11-11T12:08:42.799 Info Finished serializing Newtonsoft.Json.Tests.Serialization.Staff. Path ''.
// 2013-05-18T21:38:11.255 Verbose Serialized JSON:
// {
// "Name": "Arnie Admin",
// "StartDate": new Date(
// 976623132000
// ),
// "Roles": [
// "Administrator"
// ]
// }
|
Json.NET有两个ITraceWriter实现:MemoryTraceWriter,它将消息保存在内存中以便进行简单的调试,如上例所示,以及DiagnosticsTraceWriter,它将消息写入应用程序正在使用的任何System.Diagnostics.TraceListeners。
要使用现有的日志记录框架编写消息,只需实现ITraceWriter的自定义版本即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public class NLogTraceWriter : ITraceWriter
{
private static readonly Logger Logger = LogManager.GetLogger("NLogTraceWriter");
public TraceLevel LevelFilter
{
// trace all messages. nlog can handle filtering
get { return TraceLevel.Verbose; }
}
public void Trace(TraceLevel level, string message, Exception ex)
{
LogEventInfo logEvent = new LogEventInfo
{
Message = message,
Level = GetLogLevel(level),
Exception = ex
};
// log Json.NET message to NLog
Logger.Log(logEvent);
}
private LogLevel GetLogLevel(TraceLevel level)
{
switch (level)
{
case TraceLevel.Error:
return LogLevel.Error;
case TraceLevel.Warning:
return LogLevel.Warn;
case TraceLevel.Info:
return LogLevel.Info;
case TraceLevel.Off:
return LogLevel.Off;
default:
return LogLevel.Trace;
}
}
|