NewtonSoft.JSON官方手册中文版(整理)

原资料地址:https://www.newtonsoft.com/json/help/html/Introduction.htm

资料来源CSDN—樊潇洁

代码块只是为了好看可以忽略

NewtonSoft.JSON的特色

世界级的JSON序列化器
利用Json.NET的强大的JSON序列化器来序列化和反序列化任何.NET对象

LINQ to JSON
使用Json.NET的JObject、JArray和JValue对象创建、解析、查询和修改JSON。

JSON路径
利用某个类似XPath的语法来查询JSON。在此处可以了解到关于JSON路径的更多信息。

高性能
比DataContractJsonSerializer快50%,比JavaScriptSerializer快250%。

容易使用
Json.NET使化繁为简成为可能。

XML支持
如果你有那种需要,Json.NET还支持XML和JSON之间的转换。

开源
Json.NET是开源软件,完全免费商用。

随处可用
Json.NET 支持Windows、Windows Store、Windows Phone、Mono和Xamarin。

最流行的.NET库
超过1亿下载量和计数,Json.NET是NuGet上的下载王。

许可
Json.NET依照MIT license开源,而且可免费商用。

Json.NET是一套流行的高性能JSON框架,用于.NET。

功能和优点

  • 灵活的JSON序列化器,用于.NET对象和JSON之间的转换
  • LINQ转换为JSON,用于人为阅读和编写JSON
  • 高性能:比.NET内建的JSON序列化器更快
  • 编写有缩进的、容易阅读的JSON
  • 从JSON转换为XML,从XML转换为JSON
  • 支持.NET Standard 2.0、.NET 2、 .NET 3.5、 .NET 4、 .NET 4.5、 Silverlight、 Windows Phone 和 Windows 8 Store

如果你正在阅读或编写的JSON要映射到.NET类,JSON.NET中的JSON序列化器是一个很好的选择

LINQ to JSON适合于以下情形:您只想从JSON获取值,没有用来序列化或反序列化的类,或者JSON与您的类截然不同,您需要从对象中手动读写。

起步

  • 序列化JSON、反序列化JSON
  • LINQ to JSON
  • 范例

历史

Json.NET源于我在2005年末从事的项目,涉及JavaScript、AJAX和.NET。那个时候还没有库有来在.NET中配合JavaScript工作,所以我自己制作了一个。

Json.Net最初是两种用于转义javascript字符串的静态方法,后来它随着功能的增加而发展。为了添加对读取JSON的支持,需要一个重构,因此Json.Net被分为三个主要的类,一直沿用至今:JsonReader、JsonWriter和JsonSerializer。

Json.NET第一版在2006年6月发布。从那时起,Json.Net已经被来自世界各地的开发人员下载了数十万次。它被用于主要的开源项目,包括ASP.NET SignalR,它是一个异步库,用于生成实时的、多用户的交互式Web应用程序,以及ASP.NET Core,微软的Web应用以及服务框架。

序列化和反序列化JSON

在JSON文本和某个.NET对象之间转换,最快捷的方法是使用JsonSerializer。JsonSerializer把.NET对象转换为它们的JSON等价物,并转换回来,方法是把.NET对象的属性名映射到JSON属性名,并复制它们的值。

  • JsonConvert
  • JsonSerializer

JsonConvert

在一些情形中,你要把.NET对象转换成JSON字符串、从JSON字符串中取得.NET对象,JsonConvert中的SerializeObject()方法和DeserializeObject()方法提供了简便易用的包装器,包装了JsonSerializer。

利用JsonConvert序列化和反序列化JSON

Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonConvert.SerializeObject(product);
//{
//  "Name": "Apple",
//  "ExpiryDate": "2008-12-28T00:00:00",
//  "Price": 3.99,
//  "Sizes": [
//    "Small",
//    "Medium",
//    "Large"
//  ]
//}
Product deserializedProduct = JsonConvert.DeserializeObject(output);

SerializeObject和DeserializeObject两者都有重载方法,取用一个JsonSerializerSettings对象为参数。下面的JsonSerializerSetters让你使用很多JsonSerializer设置列表,与此同时,依然使用简单的序列化方法。

JsonSerializer

欲对如何序列化一个对象有更多的控制,可以直接使用JsonSerializer。JsonSerializer可以利用JsonTextWriter方法直接把JSON文本写成一个流,或者利用JsonTextReader方法从流中读取JSON文本。也可以使用其它类型的JsonWriter,譬如JTokenReader、JTokenWriter方法,它们把你的对象转换为LINQ,或者从LINQ中转换得JSON对象。或者BsonReader、BsonWriter方法,它们把JSON转换为BSON,或者从BSON中转换得JSON对象。

利用JsonSerializer把JSON序列化为流

Product product = new Product();
product.ExpiryDate = new DateTime(2008, 12, 28);
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new JavaScriptDateTimeConverter());
serializer.NullValueHandling = NullValueHandling.Ignore;
using (StreamWriter sw = new StreamWriter(@"c:\json.txt"))
using (JsonWriter writer = new JsonTextWriter(sw))
{
  serializer.Serialize(writer, product);
  // {"ExpiryDate":new Date(1230375600000),"Price":0}
}

JsonSerializer具有很多属性,可以用它们来自定义如何序列化JSON。通过JsonSerializerSettings重载,这些属性也可以与JsonConvert方法配合使用。

序列化设置

JsonSerializer具有很多属性,可以用它们来自定义如何序列化JSON。通过JsonSerializerSettings重载,这些属性也可以与JsonConvert方法配合使用。

  • DateFormatHandling
  • MissingMemberHandling
  • ReferenceLoopHandling
  • NullValueHandling
  • DefaultValueHandling
  • ObjectCreationHandling
  • TypeNameHandling
  • TypeNameAssemblyFormat
  • SerializationBinder
  • MetadataPropertyHandling
  • ConstructorHandling
  • Converters
  • ContractResolver
  • TraceWriter
  • Error

DateFormatHandling

DateFormatHandling 控制数据如何序列化。

成员 描述
IsoDateFormat 默认情况下,Json.Net用ISO 8601格式写日期,例如“2012-03-21T05:40Z”。
MicrosoftDateFormat 用微软JSON格式写日期,例如“\/Date(1198908717056)\/”。

MissingMemberHandling

MissingMemberHandling 控制在反序列化过程中如何处理流离的成员,例如,JSON包含了一个属性,它不是对象上的成员。

成员 描述
Ignore 默认情况下,Json.NET会忽略JSON,如果在反序列化过程中它的值没有被设置字段或属性。
Error 如果在反序列化过程中存在游离的成员,Json.Net会抛出错误。

ReferenceLoopHandling

ReferenceLoopHandling 控制循环引用对象如何序列化,例如,某个Person对象通过一个Manager属性引用它自身。

Equals(Object)方法用来测试一个对象是否在循环引用中。默认情况下,Object.Equals(Object)将用来测试引用类型的引用是否相等,值类型的私有值和公共值是否相等。类和结构可以重写此方法。

成员 描述
Error 默认情况下,Json.NET如果遭遇了引用循环,就会抛出错误(否则序列化程序将陷入无陷循环)。
Ignore Json.NET将忽略引用循环中的对象,而不序列化它们。第一次遇到这种对象,会照常序列化它,但是如果该对象是作为其自身的子对象遇到的,则序列化程序将跳过序列化它。
Serialize 此选项会强制Json.NET序列化引用循环中的对象。如果对象是嵌套的,但不是无限的,这个选项就很有用。

在调用serializer时,ReferenceLoopHandling可以作一个参数使用,可以使用ItemReferenceLoopHandling在一个对象的多个属性上或者一个集合的项上设置它,也可以利用ReferenceLoopHandling在一个属性上自定义它,或者利用ItemReferenceLoopHandling在一个属性的对象属性或集合项上自定义它。

NullValueHandling

NullValueHandling 控制了在序列化过程中,.NET对象中的null值如何处理,在反序列化过程中,JSON中的null值如何中处理。

成员 描述
Include 默认情况下,在序列化时,Json.NET把null值写到JSON值中;在反序列化时,Json.NET给字段、属性设置null值。
Ignore 在序列化时,如果.NET值是null,Json.NET将跳过写入JSON属性,在反序列化时,如果JSON属性是null,Json.NET将跳过给字段、属性设置值。

还可以利用JsonPropertyAttribute在单个属性上自定义NullValueHandling。

DefaultValueHandling

DefaultValueHandling控制了Json.NET在序列化和反序列化时,如何使用利用.NET DefaultValueAttribute设置的默认值集合。

成员 描述
Include 默认情况下,如果值与字段、属性的默认值相同,在序列化时,Json.NET将在JSON中写入一个字段属性值。如果JSON值与默认值相同,JSON.NET的反序列化器将继续设置字段、属性。
Ignore 如果值与字段、属性的默认值相同,或者与DefaultValueAttribute中指定的自定义值相同,Json.NET将跳过编写字段、属性值。如果JSON的值与默认值相同,Json.NET的反序列化器将跳过设置一个.NET对象的字段、属性。

还可以利用JsonPropertyAttribute值在单个属性上自定义DefaultValueHandling。

ObjectCreationHandling

ObjectCreationHandling控制了在反序列化过程中,如何创建对象,如何反序列化为对象。

成员 描述
Auto 默认情况下,在反序列化过程中,Json.NET将尝试把JSON值设置到已有的对象上,把JSON值添加到已有的集合中。
Reuse 与auto的行为相同。
Replace 在反序列化过程中,Json.NET将始终重新创建对象和集合,然后对它们设置值。

还可以利用JsonPropertyAttribute在单个属性上自定义ObjectCreationHandling。

TypeNameHandling

Caution
当你的应用程序从外部源反序列化JSON时,必须小心地运用TypeNameHandling。在运用除了TypeNameHandling.None之外的其它值来反序列化时,必须利用自定义的ISerializationBinder来校验输入类型。

TypeNameHandling控制了在序列化过程中,Json.NET是否利用一个$type属性来包含.NET类型的名称;在反序列化过程中,Json.NET是否从属性中读取.NET类型名称来确定创建哪种类型。

$type这样的元数据属性必须位于JSON对象的开头,从而能在反序列化过程中能被成功地侦测到。如果你不能控制JSON对象中的属性的顺序,则可以使用MetadataPropertyHandling来删除此限制。

可以通过创建自己的ISerializationBinder来自定义并校验$type属性的值。

成员 描述
None 默认情况下,在反序列化过程中,Json.NET并不读或写类型的名称。
Objects Json.NET将为对象写入并使用类型名称,但是对集合不采取同样措施。
Arrays Json.NET将为集合写入并使用类型名称,但不对对象不采取同样的措施。
Auto Json.NET将检查某个对象、集合是否匹配它的声明属性,如果不匹配,就写入类型名称,例如,其类型为Mammal的属性被赋值了一个派生的实例Dog。Auto将确保在序列化、反序列化过程中,类型信息自动不会丢失,而不需要为每个对象写入类型名称。
All Json.NET将为对象和集合写入并使用类型名称。

在调用序列化器时,可以将TypeNameHandling用作参数,可以利用ItemTypeNameHandling在一个对象的属性或一个集合的项上设置它,可以利用TypeNameHandling在某个属性上自定义它,可以利用ItemTypeNameHandling在一个属性的对象属性或集合项上设置它。

TypeNameAssemblyFormat

FormatterAssemblyStyle控制了在序列化过程中如何编写类型名称。

成员 描述
Simple 默认情况下,Json.NET利用类型来写入部分程序集名称,例如System.Data.DataSet、System.Data。请注意,Silverlight和Windows Phone不能使用这种格式。
Full Json.NET将写入完整的程序集名称,包括版本数、语言版本和公钥标记。

请在FormatterAssemblyStyle读取有效的值。

SerializationBinder

在序列化过程中,ISerializationBinder用来把.NET类型换算为类型名称,在反序列化过程中,它用来把类型名称换算成.NET类型。

如果启用了TypeNameHandling,则强烈建议使用某个自定义的ISerializationBinder来校验输入类型名称,为安全起见。

MetadataPropertyHandling

MetadataPropertyHandling控制了在反序列化过程中,如何读取像$type$id这样的元数据属性。

出于性能原因,默认情况下,JsonSerializer假定任何元数据属性都位于JSON对象的开头。如果你不能够保证JSON中属性的序列,你可以先反序列化,然后使用MetadataPropertyHandling.ReadAhead来删除此限制,它需要付出一定的性能成本。

成员 描述
Default 默认情况下,Json.NET将只能读取JSON对象开头的元数据属性。
ReadAhead Json.NET将查找遍位于JSON对象的任意位置的元数据属性。
Ignore Json.NET将忽略元数据属性。

ConstructorHandling

ConstructorHandling控制了在反序列化过程中如何使用构造器来初始化对象。

成员 描述
Default 默认情况下,Json.NET将首先查找利用JsonConstructorAttrubite标记的构造器,然后查找公共构造器(一个构造器,不取用任何参数),然后检查类是否有单个公共构造器,带有参数,最后检查非公共的默认构造器。如果类有多个公共构造器带有参数,则将抛出一个错误。可以通过对其中一个构造器标记JsonConstructorAttrubite来修复这种问题。
AllowNonPublicDefaultConstructor 在使用带参数的构造器之前,如果可以的话,Json.NET将使用一个类私有默认构造器。

Converters

这是JsonConverters的集合,将在序列化和反序列化过程中使用它们。

JsonConverter允许在序列化过程中手动编写JSON、在反序列化中手动读取JSON。对于比较复杂的JSON结构,这很有用;此外,如果你想要改变一个类型如何被序列化,它也很有用。

如果某个JsonSerializer添加了一个JsonConverter,它会利用它的ConConvert来检查要被序列化、反序列化的每一个值,看是否应该用到它。如果CanConvert返回true,则JsonConverter将用来从JSON中读取那个值,或把那个值写入JSON。请注意,虽然JsonConverter给了你对JSON值的完全的控制,但这也意味着很多Json.NET序列化功能也不再可用了,譬如类型名称和引用处理。

在调用序列化器时,JsonConverters可被用作一个参数,可以利用JsonConverterAttribute在一个对象或属性上设置它,可以利用ItemConverterType在一个对象的属性或一个集合的项上设置它,可以利用ItemConverterType在一个属性的对象属性或集合项上设置它。

若要创建你的自定义转换器,继承自JsonConverter类,请阅读下面的关于内建JsonConverters的文章:

  • 在JSON中序列化Dates
  • 在JSON和XML之间转换
  • CustomCreationConverter
  • StringEnumConverter

ContractResolver

在内部,JsonSerializer将根据类型元数据和应用于类的特性,为每个.NET类型创建一个类型应如何序列化和反序列化的协定。指定一个自定义的IContractResolver允许创建自定义的协定。

此处可以进一步了解协定换算器:利用ContractResolver序列化

TraceWriter

利用ITraceWriter接口,Json.NET序列化器支持日志和调试。通过分配一个跟踪编写器,在序列化JSON和反序列化JSON时,你可以调试在Json.NET序列化器内部发生的事情。

在此处阅进一步了解TraceWriter:利用序列化跟踪来调试

Error

Error事件可以捕获序列化过程中的错误,既可以处理错误,也可以继续序列化,或让错误冒泡,从而抛出给应用程序。

序列化教程

Json.NET序列化器可以序列化各种各样的.NET对象。此教程着眼于它如何工作,先讲高层次,后深入细节。

  • 摘要
  • 复合类型
  • 基元类型
  • 类型序列化的分解

摘要

在高层次,Json.NET序列化器将把基元.NET值转换为基元JSON值,将把.NET数组和集合转换为JSON数组,将把其它的一切转换为JSON对象。

在反序列化一个值时,如果Json.NET遭遇了不正确的JSON,它将抛出一个错误。例如,如果序列化器遭遇了一个JSON属性,带有一个值的数组,而且匹配.NET属性的类型不是一个集合,则将抛出一个错误,反之亦然。

复合类型

.NET JSON
IList, IEnumerable, IList, Array Array (集合上的属性不能被序列化)
IDictionary, IDictionary Object (只有字典名、值,不会序列化字典上的属性)
Object (more detail below) Object

基元类型

.NET JSON
String String
Byte SByte UInt16 Int16 UInt32 Int32 UInt64 Int64 Integer
Float Double Decimal Float
Enum Integer (can be the enum value name with StringEnumConverter)
DateTime String (Serializing Dates in JSON)
Byte[] String (base 64 encoded)
Type String (type name)
Guid String
TypeConverter (convertible to String) String

类型序列化的分解

此节内容包含了下面的小节:

  • Objects
  • IEnumerable, Lists, and Arrays
  • Dictionaries and Hashtables
  • Untyped Objects
  • Dynamic
  • ISerializable
  • LINQ to JSON
  • JsonConverter

Objects

不属于下面列出的任何其他类别的.NET类型(即不是列表、字典、动态、实现ISerializable等)将被序列化为JSON对象。你还可以通过把JsonObjectAttribute放到类型上来强迫一个类型被序列化为JSON。

默认情况下,一个类型的属性会在opt-out模式下被序列化。这意味着,所有带有getter的公共字段和属性都会自动序列化为JSON,而不应该序列化的字段和属性则会通过在其上放置JsonIgnoreAttribute来进行选择。若要序列化私有成员,可以在私有字段和属性上放置JsonPropertyAttribute。

也可以使用opt-in模式来序列化类型。只有具有JsonPropertyAttribute或DataMemberAttribute的属性或字段将被序列化。对象的Opt-in模式是通过在类型上放置JsonObjectAttribute或DataContractAttribute来指定的。

最后,可以使用字段模式来序列化炻工。所有的字段,无论是公开的还是私有的,都被序列化,所有属性都被忽略。这可以通过利用JsonObjectAttribute在类型上设置MemberSerialization.Fields来指定,或者使用.NET SerializableAttribute并在DefaultContractResolver上把IgnoreSerializableAttribute设置为false来指定。

IEnumerable, Lists, and Arrays

.NET列表(类型继承自IEnumerable)以及.NET数组会被转换为JSON数组。因为JSON数组只支持值的范围,而不支持属性,所有声明在.NET集合上的任何额外的属性和字段都不会被序列化。在某些情形中,类型实例化了IEnumerable,但是不想要JSON数组,则可以在类型上放置JsonObjectAttribute,以迫使它被序列化为一个JSON对象。

JsonArrayAttribute上面有选项,用来自定义应用到集合项上的JsonConverter、类型名称处理和引用处理。

请注意,如果在序列化器上已经针对JSON数组启用了TypeNameHandling或PreserveReferencesHandling,则JSON数组将被包装入一个容纳对象。此对象将具有类型名称、引用属性以及一个$value属性,它具有集合的数据。

在序列化时,如果某个成员被类型化为接口 IList,则它将被反序列化为List。

你可以在此处进一步了解序列化集合:序列化集合

字典和哈希表

.NET字典(继承自IDictionary的类型)被转换为JSON对象。请注意,在序列化时,只有字典名称、值会被写到JSON对象中,在反序列化时,JSON对象上的属性将被添加到字典的名称值上。在序列化时,.NET字典上的额外的成员会被忽略。

当序列化一个字典时,字典的键被转换为字符串,用作JSON对象属性名称。可以自定义写作键的字符串,既可以通过针对键类型重写ToString(),也可以通过TypeConverter来实现。TypeConverter也支持在反序列化一个字典时,把自定义的字符串转换回去。

JsonDictionaryAttribute上面有选项,用来自定义应用到集合项上的JsonConverter,类型名称处理以及引用处理。

在反序列化时,如果一个成员被类型化为接口IDictionary,则它将被反序列化为Dictionary

你可以在此处进一步了解序列化集合:序列化集合

未类型化的对象

在类上的.NET属性,如果没有指定类型(亦即,它们只是对象)也会如常序列化。当未类型化的属性被反序列化时,序列化器没有办法知道要创建哪种类型(除非启用了类型名称处理,而且JSON包含了类型名称)。

对于那些未类型化的属性,Json.NET序列化器将JSON读入LINQ to JSON对象,并把它们设置为属性。将针对JSON对象创建JObject;将针对JSON数组创建JArray,针针对基元JSON值创建JValue。

动态

.NET中有两种不同的动态用法(在.NET 4中引入)。第一种是.NET属性,带有动态的类型。动态属性行为像属性,被声明为对象:可以给它分配一些值,但是区别是可以在动态的属性上调用属性和方法,无需强制转换。在Json.NET中,动态属性被序列化、反序列化,与非类型化的对象完全一致:因为动态不是实际的类型,Json.NET会回退到将Json反序列化为LINQ to Json对象。

在.NET中动态的第二种用法是由实现 IDynamicMetaObjectProvider的类型来实现的。此接口让实现器创建动态对象,截断在一个对象上调用的属性和方法,并使用它们。ExpandoObject是动态对象的好例子。

动态对象会被序列化为JSON对象。针对DynamicMetaObject.GetDynamicMemberNames()返回的所有成员编写属性。动态对象的常规属性默认不会被序列化,但是可以被包含,只要在它上面放JsonPropertyAttribute。

在反序列化动态对象时,序列化器首先尝试在名称匹配的常规.NET成员上设置JSON属性值。如果找不到带有那个名称的.NET成员,则序列化器将在动态对象上调用SetMember。因为动态对象上的动态成员不存在类型信息,所以分配给他们的值将是LINQ to JSON对象。

ISerializable

实现ISerializable的类型,以及带有SerializableAttribute标记的类型会被序列化为JSON对象。在序列化时,只用到了返回自ISerializable.GetObjectData的值;类型上的成员会被忽略。在反序列化时,调用了带有SerializationInfo和StreamingContext的构造器,传递JSON对象的值。

在一些不需要这种行为的情形中,可在.NET类型上面放置JsonObjectAttribute,实现Iserializable强制它被序列化为一个常规JSON对象。

LINQ to JSON

当Json.NET序列化器遭遇到了LINQ to JSON类型时,LINQ to JSON类型(例如,JObject和JArray)会自动序列化和反序列化为和它们等价的JSON。

JsonConverter

JsonConvert完全重写了可被JsonConverter转换的值(亦即,对于那种类型,CanConvert返回true)的序列化。检查JsonSerializer是否可以转换值的测试优先于所有其他测试。

可以在很多地方定义并指定JsonConverters:在某个成员的某个特性上、在一个类的特性上,或者添加到JsonSerializer的转换器集合上。使用哪种JsonConvert的优先顺序,首先一个成员的特定上定义的JsonConverter,然后是一个类上定义的JsonConverter,最后是传给JsonSerializer的任何转换器。

序列化特性

可以用来控制Json.NET如何序列化和反序列化.NET对象的特性。

  • JsonObjectAttribute - 放置在一个类上,以控制如何把类序列化为JSON对象。
  • JsonArrayAttribute - 放置在一个集合上,以控制如何把集合序列化为JSON数组。
  • JsonDictionaryAttribute - 放置在字典上,以控制如何把字典序列化为JSON对象。
  • JsonPropertyAttribute - 放置在字段和属性主,以控制如何把字段和属性序列化为JSON对象中的一个属性。
  • JsonConverterAttribute - 要么放置在类上,要么放置在字段和属性上,以指定在序列化过程中要使用哪种JsonConverter。
  • JsonExtensionDataAttribute - 放置在集合字段或属性上,以将没有匹配类成员的属性反序列化到指定的集合中,并在序列化期间写入值。
  • JsonConstructorAttribute - 放置在一个构造器上,指定在反序列化过程中,需要用它来创建类。

标准.NET序列化特性

除了使用内建的Json.NET特性,Json.NET还会查找SerializableAttribute(如果DefaultContractResolver上的IgnoreSerializableAttribute被设置为false)、DataContractAttribute、DataMemberAttribute和NonSerializedAttribute,以及用来确定如何序列化和反序列化JSON的一些特性。

Note
Json.NET特性优先于标准的.NET序列化特性(例如,如果一个属性上同时出现了JsonPropertyAttribute和DataMemberAttribute,两者都自定义了其名称,则来将使用来自JsonPropertyAttribute的名称)

序列化特性示例

[JsonObject(MemberSerialization.OptIn)]
public class Person
{
  // "John Smith"
  [JsonProperty]
  public string Name { get; set; }
  // "2000-12-15T22:11:03"
  [JsonProperty]
  public DateTime BirthDate { get; set; }
  // new Date(976918263055)
  [JsonProperty]
  public DateTime LastModified { get; set; }
  // not serialized because mode is opt-in
  public string Department { get; set; }
}

Json.NET序列化特性

此节内容包含了以下小节:

  • JsonObjectAttribute
  • JsonArrayAttribute/JsonDictionaryAttribute
  • JsonPropertyAttribute
  • JsonIgnoreAttribute
  • JsonConverterAttribute
  • JsonExtensionDataAttribute
  • JsonConstructorAttribute

JsonObjectAttribute

此特性上的MemberSerialization标志指定了成员序列化到底是opt-in的(必须是具有JsonPropery或DataMember特性的成员才会被序列化),还是opt-out的(默认每个成员都会被序列化,但是带有JsonIgnoreAttribute的成员会被忽略,这是Json.NET的默认行为),还是字段(所有公开的和私有的字段会被序列化,但是属性会被忽略)。

在类型上放 DataContractAttribute是默认成员序列化为opt-in的另一种方式。

此特性上的NamingStrategy设置,可被设置为某个NamingStrategy类型,指定了如何序列化属性名称。

Json.NET将实现IEnumerable的.NET类序列化为JSON数据,用IEnumerable的值来填充。放置JsonObjectAttribute会覆盖此行为,并强迫序列化器序列化类的字段和属性。

JsonArrayAttribute/JsonDictionaryAttribute

JsonArrayAttribute和JsonDictionaryAttribute用来指定类是否被序列化为集合类型。

集合特性具有选项来自定义JsonConverter,类型名称处理,以及应用到集合项的引用的处理。

JsonPropertyAttribute

JsonPropertyAttribute具有很多用法:

  • 默认情况下,JSON属性具有与.NET属性相同的名称。此特性允许自定义名称。
  • JsonPropertyAttribute表明,当成员被序列化为opt-in时,属性应该被序列化。
  • 它包含了在序列化和反序列化处理中的非公开的属性。
  • 它可被用来自定义类型名称、引用、空,以及处理属性值的默认值。
  • 它可以用来自定义序列化属性名称的NamingStrategy。
  • 它可以用来自定义属性集合项JsonConverter,类型名称处理,以及引用处理。

DataMemberAttribute可用作JsonPropertyAttribute的替代品。

JsonIgnoreAttribute

从序列化中排除了一个字段或属性。

NonSerializedAttribute可用作JsonIgnoreAttribute的替代品。

JsonConverterAttribute

JsonConverterAttribute指定了用哪种JsonConverter来转换对象。

此属性既可以放在一个类上,也可以放在一个成员上。当放置在一个类上时,由此属性指定的JsonConverter将是序列化类的默认方式。此特性放在一个字段或属性上时,则指定的JsonConverter将始终有来指定那个值。

使用哪种JsonConverter的优先级顺序,首先是成员特性,然后是类特性,最后是任何传给JsonSerializer的转换器。

JsonConverterAttribute属性示例

public enum UserStatus
{
  NotConfirmed,
  Active,
  Deleted
}
public class User
{
  public string UserName { get; set; }
  [JsonConverter(typeof(StringEnumConverter))]
  public UserStatus Status { get; set; }
}

此示例演示了JsonConverterAttribute可以应用到一个属性上。

要将一个JsonConverter应用到某个集合上的项,既可以使用JsonArrayAttribute、JsonDictionaryAttribute,又可以使用JsonPropertyAttribute,并把ItemConverterType属性设置为你想要使用的转换器类型。

JsonExtensionDataAttribute

JsonExtensionDataAttribute指示JsonSerializer将类型上没有匹配字段或属性的属性反序列化到指定的集合中。在序列化过程中,此集合中的值被写回实例的JSON对象。

Note
序列化过程中将写入所有的扩展数据,哪怕已经写入了同名的属性。

此示例显示了一个字段上应用了JsonExtensionDataAttribute,在反序列化过程中,不匹配的JSON属性被添加到字段的集合。

类型

public class DirectoryAccount
{
  // normal deserialization
  public string DisplayName { get; set; }
  // these properties are set in OnDeserialized
  public string UserName { get; set; }
  public string Domain { get; set; }
  [JsonExtensionData]
  private IDictionary _additionalData;
  [OnDeserialized]
  private void OnDeserialized(StreamingContext context)
  {
      // SAMAccountName is not deserialized to any property
      // and so it is added to the extension data dictionary
      string samAccountName = (string)_additionalData["SAMAccountName"];
      Domain = samAccountName.Split('\\')[0];
      UserName = samAccountName.Split('\\')[1];
  }
  public DirectoryAccount()
  {
      _additionalData = new Dictionary();
  }
}

用法

string json = @"{
  'DisplayName': 'John Smith',
  'SAMAccountName': 'contoso\\johns'
}";
DirectoryAccount account = JsonConvert.DeserializeObject(json);
Console.WriteLine(account.DisplayName);
// John Smith
Console.WriteLine(account.Domain);
// contoso
Console.WriteLine(account.UserName);
// johns

JsonConstructorAttribute

JsonConstructorAttribute指示JsonSerializer在反序列化一个类时使用指定的构造器。可以用它来创建一个类,使用一个参数化的构造器,而不是默认构造器,或者如果存在多个构造器,则挑选使用哪个特定参数化的构造器。

类型

public class User
{
  public string UserName { get; private set; }
  public bool Enabled { get; private set; }
  public User()
  {
  }
  [JsonConstructor]
  public User(string userName, bool enabled)
  {
      UserName = userName;
      Enabled = enabled;
  }
}

用法

string json = @"{
  ""UserName"": ""domain\\username"",
  ""Enabled"": true
}";
User user = JsonConvert.DeserializeObject(json);
Console.WriteLine(user.UserName);
// domain\username

序列化回调函数

Json.NET支持序列化回调函数方法。在JsonSerializer序列化一个对象之前或之后、反序列化一个对象之前或之后,回调函数可以用来操作这个对象。

  • OnSerializing
  • OnSerialized
  • OnDeserializing
  • OnDeserialized

若要告诉序列化器在对象的序列化生命周期内需要调用哪种方法,只要用适当的特性(OnSerializingAttribute、OnSerializedAttribute、OnDeserializingAttribute、OnDeserializedAttribute)来布局一个方法

示例

示例对象以及序列化回调方法的:

序列化回调特性

public class SerializationEventTestObject
{
  // 2222
  // This member is serialized and deserialized with no change.
  public int Member1 { get; set; }
  // The value of this field is set and reset during and
    
  // after serialization.
  public string Member2 { get; set; }
  // This field is not serialized. The OnDeserializedAttribute
    
  // is used to set the member value after serialization.
  [JsonIgnore]
  public string Member3 { get; set; }
  // This field is set to null, but populated after deserialization.
  public string Member4 { get; set; }
  public SerializationEventTestObject()
  {
      Member1 = 11;
      Member2 = "Hello World!";
      Member3 = "This is a nonserialized value";
      Member4 = null;
  }
  [OnSerializing]
  internal void OnSerializingMethod(StreamingContext context)
  {
      Member2 = "This value went into the data file during serialization.";
  }
  [OnSerialized]
  internal void OnSerializedMethod(StreamingContext context)
  {
      Member2 = "This value was reset after serialization.";
  }
  [OnDeserializing]
  internal void OnDeserializingMethod(StreamingContext context)
  {
      Member3 = "This value was set during deserialization";
  }
  [OnDeserialized]
  internal void OnDeserializedMethod(StreamingContext context)
  {
      Member4 = "This value was set after deserialization.";
  }
}

此示例中Json.NET序列化并反序列化了对象:

序列化回调示例

SerializationEventTestObject obj = new SerializationEventTestObject();
Console.WriteLine(obj.Member1);
// 11
Console.WriteLine(obj.Member2);
// Hello World!
Console.WriteLine(obj.Member3);
// This is a nonserialized value
Console.WriteLine(obj.Member4);
// null
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
// {
//   "Member1": 11,
//   "Member2": "This value went into the data file during serialization.",
//   "Member4": null
// }
Console.WriteLine(obj.Member1);
// 11
Console.WriteLine(obj.Member2);
// This value was reset after serialization.
Console.WriteLine(obj.Member3);
// This is a nonserialized value
Console.WriteLine(obj.Member4);
// null
obj = JsonConvert.DeserializeObject(json);
Console.WriteLine(obj.Member1);
// 11
Console.WriteLine(obj.Member2);
// This value went into the data file during serialization.
Console.WriteLine(obj.Member3);
// This value was set during deserialization
Console.WriteLine(obj.Member4);
// This value was set after deserialization.

序列化错误处理

Json.NET支持序列化和反序列化过程中的错误处理。错误处理让你能够捕获错误,并选择是否处理错误,并继续序列化,或让错误在应用程序中冒泡并抛出错误。

通过两种方法定义了错误处理:在JsonSerializer上的Error事件以及OnErrorAttribute。

  • Error Event
  • OnErrorAttribute

rror事件

Error事件是一个事件处理器,能在JsonSerializer上找到。每当在序列化JSON或反序列化JSON时抛出了一个异常,就引发了此事件。就像在JsonSerializer上能找到的所有设置,Error也被设置在JsonSerializerSettings,并传递给JsonConvert上的序列化方法。

序列化错误处理

List errors = new List();
List c = JsonConvert.DeserializeObject>(@"[
    '2009-09-09T00:00:00Z',
    'I am not a date and will error!',
    [
      1
    ],
    '1977-02-20T00:00:00Z',
    null,
    '2000-12-01T00:00:00Z'
  ]",
  new JsonSerializerSettings
  {
      Error = delegate(object sender, ErrorEventArgs args)
      {
          errors.Add(args.ErrorContext.Error.Message);
          args.ErrorContext.Handled = true;
      },
      Converters = { new IsoDateTimeConverter() }
  });
// 2009-09-09T00:00:00Z
// 1977-02-20T00:00:00Z
// 2000-12-01T00:00:00Z
// The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.
// Unexpected token parsing date. Expected String, got StartArray.
// Cannot convert null value to System.DateTime.

在此示例中,我们把一个JSON对象反序列化为一个DataTimes的集合。在JsonSerializerSettings上,可以给Error事件分配一个处理函数,它会记录一条消息,并标记此错误已被处理。

反序列化此JSON的结果是三条成功的反序列化日期,以及三条错误消息:一要是针对格式错误的字符串(“I am not a date and will error”),一条是针对嵌套的JSON数组,一条是针对null值,因为列表不允许可空的DateTimes。此事件处理器记录了这三条消息,Json.NET没有停止反序列化JSON,是因为错误已被标记为已处理。

Json.NET中的错误处理,有一件需要注意的事情是,未处理的错误将会冒泡,并在它的每个你对象上引发错误事件上。例如,在序列化一个对象的集合时,一个未处理的错误将被引发两次,一次是针对此对象引发,另一次发生在集合上。从而你可以在发生错误的对象上或它的任一个父级上处理错误。

父级错误处理

List errors = new List();
JsonSerializer serializer = new JsonSerializer();
serializer.Error += delegate(object sender, ErrorEventArgs args)
{
  // only log an error once
  if (args.CurrentObject == args.ErrorContext.OriginalObject)
  {
      errors.Add(args.ErrorContext.Error.Message);
  }
};

如果你不能立即处理错误,只想针对它实施一次动作,则你可以检查ErrorEventArgs的CurrentObject是否等一OriginalObject。OriginalObject是抛出错误的对象,CurrentObject是引发事件的对象。只有在第一次针对OriginalObject引发事件时,这两者才是相等的。

OnErrorAttribute

OnErrorAttribute的作用方式很像Json.NET支持的其它.NET序列化特性。若要使用它,你只要把该特性放到方法上,并取用正确的参数:一个StreamingContext和一个ErrorContext。方法的名称是无关紧要的。

序列化错误处理特性

public class PersonError
{
  private List _roles;
  public string Name { get; set; }
  public int Age { get; set; }
  public List Roles
  {
      get
      {
          if (_roles == null)
          {
              throw new Exception("Roles not loaded!");
          }
          return _roles;
      }
      set { _roles = value; }
  }
  public string Title { get; set; }
  [OnError]
  internal void OnError(StreamingContext context, ErrorContext errorContext)
  {
      errorContext.Handled = true;
  }
}

在此示例如,当没有设置roles时,访问Roles属性将抛出一个异常。在序列化Roles的时候,此HandleError方法将把错误设置为已处理,并让Json.NET继续序列化类。

序列化错误处理示例

PersonError person = new PersonError
{
  Name = "George Michael Bluth",
  Age = 16,
  Roles = null,
  Title = "Mister Manager"
};
string json = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(json);
//{
//  "Name": "George Michael Bluth",
//  "Age": 16,
//  "Title": "Mister Manager"
//}

保留对象引用

默认情况下,Json.NET将按值序列化它遇到的所有对象。如果一个列表包含了两个Person引用,两个引用指向同一个对象,则JsonSerializer将为每个引用写出所有的名称和值。

保留对象引用关闭

Person p = new Person
{
  BirthDate = new DateTime(1980, 12, 23, 0, 0, 0, DateTimeKind.Utc),
  LastModified = new DateTime(2009, 2, 20, 12, 59, 21, DateTimeKind.Utc),
  Name = "James"
};
List people = new List();
people.Add(p);
people.Add(p);
string json = JsonConvert.SerializeObject(people, Formatting.Indented);
//[
//  {
//    "Name": "James",
//    "BirthDate": "1980-12-23T00:00:00Z",
//    "LastModified": "2009-02-20T12:59:21Z"
//  },
//  {
//    "Name": "James",
//    "BirthDate": "1980-12-23T00:00:00Z",
//    "LastModified": "2009-02-20T12:59:21Z"
//  }
//]

在大多数情况下,这就是想要的结果,但是在特定的情形中,把列表中的第二个项写为第一个项的引用是更好的解决方案。如果上面的JSON现在被反序列化,则返回的列表将包含两个完整的分开的Person对象,带有相同的值。在发生循环引用的对象上,按值写引用将导致出问题。

  • PreserveReferencesHandling
  • IsReference
  • IReferenceResolver

PreserveReferencesHandling

设置PreserveReferencesHandling将在序列化JSON和反序列化时跟踪对象引用。

保留对象引用打开

string json = JsonConvert.SerializeObject(people, Formatting.Indented,
  new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
//[
//  {
//    "$id": "1",
//    "Name": "James",
//    "BirthDate": "1983-03-08T00:00Z",
//    "LastModified": "2012-03-21T05:40Z"
//  },
//  {
//    "$ref": "1"
//  }
//]
List deserializedPeople = JsonConvert.DeserializeObject>(json,
  new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
Console.WriteLine(deserializedPeople.Count);
// 2
Person p1 = deserializedPeople[0];
Person p2 = deserializedPeople[1];
Console.WriteLine(p1.Name);
// James
Console.WriteLine(p2.Name);
// James
bool equal = Object.ReferenceEquals(p1, p2);
// true

列表中的第一个Person被序列化为附加了一个对象ID。JSON中的第二个Person现在只是对第一个Person的引用。

把PreserveReferencesHandling打开,现在反序列化时只创建了一个对象,列表包含了两个对它的引用,镜像了开始的那种情况。

像$id这样的元数据属性必须放置在JSON对象的开始,从而在反序列化过程中能被成功地探测到。如果你不能控制JSON对象中属性的顺序,则MetadataPropertyHandling可以用来删除此限制。

Note
如果值是通过非默认构造函数设置的,就无法保留引用。利用非默认构造器,子值必须在父值之前创建,从而它们可以传入到构造器,使跟踪引用变得不可能。ISerializable类型是一个类的示例,其值是用非默认构造器填充的,所以它不能配合PreserveReferencesHandling起作用。

IsReference

设置在JsonSerializer上的PreserveReferencesHandling将改变所有对象序列化、反序列化的方式。若要更细粒地控制哪些对象和成员应该被序列化为引用,则可以使用JsonObjectAttribute、JsonArrayAttribute和JsonPropertyAttribute上的IsReference属性。

把JsonObjectAttribute或JsonArrayAttribute上的IsReference设置为true将意味着JsonSerializer总是将属性所针对的类型序列化为引用。把JsonPropertyAttribute上的IsRfereence设置为true将只把那个属性序列化为引用。

IsReference

[JsonObject(IsReference = true)]
public class EmployeeReference
{
  public string Name { get; set; }
  public EmployeeReference Manager { get; set; }
}

IReferenceResolver

若要自定义如何生成引用、如何解析引用,可以从JsonSerializer继承IReferenceResolver接口并将其与JsonSerializer一起使用。

CustomCreationConverter

CustomCreationConverter 是一个JsonConverter,它提供了一种方式来自定义在JSON反序列化过程中如何创建对象。一旦创建了对象,序列化器将在其上填充值。

示例

CustomCreationConverter

public interface IPerson
{
  string FirstName { get; set; }
  string LastName { get; set; }
  DateTime BirthDate { get; set; }
}
public class Employee : IPerson
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public DateTime BirthDate { get; set; }
  public string Department { get; set; }
  public string JobTitle { get; set; }
}
public class PersonConverter : CustomCreationConverter
{
  public override IPerson Create(Type objectType)
  {
      return new Employee();
  }
}

这是一个极其简单的示例。更复杂的场景可能涉及在运行时解析对象的对象工厂或服务定位器。

CustomCreationConverter示例

//[
//  {
//    "FirstName": "Maurice",
//    "LastName": "Moss",
//    "BirthDate": "1981-03-08T00:00Z",
//    "Department": "IT",
//    "JobTitle": "Support"
//  },
//  {
//    "FirstName": "Jen",
//    "LastName": "Barber",
//    "BirthDate": "1985-12-10T00:00Z",
//    "Department": "IT",
//    "JobTitle": "Manager"
//  }
//]
List people = JsonConvert.DeserializeObject>(json, new PersonConverter());
IPerson person = people[0];
Console.WriteLine(person.GetType());
// Newtonsoft.Json.Tests.Employee
Console.WriteLine(person.FirstName);
// Maurice
Employee employee = (Employee)person;
Console.WriteLine(employee.JobTitle);
// Support

序列化集合

Json.NET对序列化对象集合和反序列化对象集合提供了卓越的支持。

  • 序列化集合
  • 反序列化集合
  • 反序列化字典

序列化集合

若要序列化一个集合——一个泛型列表、数组、字典或你自己的自定义集合——只要调用序列化器,带上你想要得到JSON的对象。Json.NET将序列化此集合,以及它包含的所有的值。

序列化集合

Product p1 = new Product
{
  Name = "Product 1",
  Price = 99.95m,
  ExpiryDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc),
};
Product p2 = new Product
{
  Name = "Product 2",
  Price = 12.50m,
  ExpiryDate = new DateTime(2009, 7, 31, 0, 0, 0, DateTimeKind.Utc),
};
List products = new List();
products.Add(p1);
products.Add(p2);
string json = JsonConvert.SerializeObject(products, Formatting.Indented);
//[
//  {
//    "Name": "Product 1",
//    "ExpiryDate": "2000-12-29T00:00:00Z",
//    "Price": 99.95,
//    "Sizes": null
//  },
//  {
//    "Name": "Product 2",
//    "ExpiryDate": "2009-07-31T00:00:00Z",
//    "Price": 12.50,
//    "Sizes": null
//  }
//]

反序列化集合

若要把JSON反序列化为一个.NET集合,只需要指定你想要反序列化为的集合的类型。Json.NET支持各种各样的集合类型。

反序列化集合

string json = @"[
  {
  'Name': 'Product 1',
  'ExpiryDate': '2000-12-29T00:00Z',
  'Price': 99.95,
  'Sizes': null
  },
  {
  'Name': 'Product 2',
  'ExpiryDate': '2009-07-31T00:00Z',
  'Price': 12.50,
  'Sizes': null
  }
]";
List products = JsonConvert.DeserializeObject>(json);
Console.WriteLine(products.Count);
// 2
Product p1 = products[0];
Console.WriteLine(p1.Name);
// Product 1

序列化字典

使用Json.NET让你还能够把一个JSON对象反序列化为一个.NET泛型字典。JSON对象的属性名称和值将被添加到此字典。

反序列化字典

string json = @"{""key1"":""value1"",""key2"":""value2""}";
Dictionary values = JsonConvert.DeserializeObject>(json);
Console.WriteLine(values.Count);
// 2
Console.WriteLine(values["key1"]);
// value1

在JSON中序列化日期

JSON中的日期时间是很难的。

这个问题来自于JSON规范文档本身:JSON中没有针对日期的字面值语法。此规范文档具有对象、数组、字符串、整型数以及浮点数,但是它没有为日期应该如何表示定义标准。

  • 日期和Json.NET
  • DateTime JsonConverters
  • JavaScriptDateTimeConverter
  • IsoDateTimeConverter

日期和Json.NET

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,针把所有的DateTimes序列化为UTC日期。请注意,此设置不影响DateTimeOffsets。

如果你不想遵守ISO 8601标准,则DateFormatString设置可以用来自定义日期字符串的格式,使用.NET的自定义日期和时间格式语法来读写日期字符串。

DateTime JsonConverters

因为JSON中没有日期标准,所以与其他系统交互时可能出现的不同格式的数量是无止境的。幸运的是Json.NET已经有了一个解决方案来处理读写自定义日期:JsonConverters。JsonConverter用来覆盖序列化某个类型的方式。

DateTime JsonConverters示例

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

JavaScriptDateTimeConverter类是Json.NET附带的两个DateTime JsonConverter之一。此转换器会把一个DateTime序列化为一个JavaScript Date对象:new Date(1234656000000)

从技术上讲,根据规范,这是无效的JSON,但是所有浏览器和一些JSON框架(包括JSON.NET)都支持它。

IsoDateTimeConverter

Caution
Json.NET 4.5及以后的.NET版本中,默认情况下日期是使用ISO 8601格式编写的,所以不需要使用此转换器。

IsoDateTimeConverter会把DateTime序列化为ISO 8601 格式的字符串:“2009-02-15T00:00:00Z”。

IsoDateTimeConverter类具有一个属性DateTimeFormat,以进一步自定义格式化字符串。

缩小序列化的JSON的大小

在把.NET对象序列化为JSON时可能的一个常见的问题是,JSON最终包含了很多不想要的属性和值。在把JSON返回到客户端时,它可能会特别显著。更大的JSON意味着占用更大的带宽和产生更慢的网站。

为了解决不想要的JSON的问题,Json.NET有很多的内置选项,来细调序列化对象中会写入哪些内容。

  • JsonIgnoreAttribute和DataMemberAttribute
  • 格式化
  • 空值处理
  • 默认值处理
  • IContractResolver

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 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 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 CreateProperties(Type type, MemberSerialization memberSerialization)
  {
      IList 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
// }

反序列化部分JSON片段

通常,在操作大型的JSON文档时,你可能只对一小段信息感兴趣。当你想把JSON片段反序列化为.NET对象时,这种情形可能会比较烦人,因为你不得不针对整个JSON结果定义.NET类。

利用Json.NET很容易变通地解决此问题。使用LINQ to JSON你可以先提取你想要反序列化的Json的小块,再把它们传给Json.NET序列化器。

片段对象

public class SearchResult
{
  public string Title { get; set; }
  public string Content { get; set; }
  public string Url { get; set; }
}

反序列化部分JSON片段的示例

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 results = googleSearch["responseData"]["results"].Children().ToList();
// serialize JSON results into .NET objects
IList searchResults = new List();
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
  • IContractResolver

ShouldSerialize

若要有条件地序列化一个属性,添加一个方法,带有和属性相同的名称,并返回布尔值,然后方法名称加前缀ShouldSerialize。此方法的结果确定了属性是否要序列化。如果此方法返回true,则属性将会被序列化,如果它返回false,则属性会被跳过。

带有ShouldSerialize方法的Employee类

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);
  }
}

ShouldSerialize 输出

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

还可以利用IContractResolver来设置ShouldSerialize。如果你不想要把一个ShouldSerialize方法放到类上,如果你不想声明类,如果你不能声明类,利用IContractResolver实现条件化地序列化一个属性是很有用的。

带有IContractResolver的条件属性

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;
  }
}

使用Serialization序列化

IContractResolver接口提供了一种自定义方式,自定义JsonSerializer如何把.NET对象序列化为JSON,以及如何把JSON反序列化为.NET对象,而不需要在你的类上放置任何特性。

任何可以在对象、集合、属性等元素上设置的、使用特性或方法来控制序列化的功能,使用一个IContractResolver来设置。

  • DefaultContractResolver
  • CamelCasePropertyNamesContractResolver
  • Custom IContractResolver Examples
Note
为了提高性能,你应该创建一次契约解析器,并尽可能重用实例。解析契约很慢,而且IContractResolver的实现通常会缓存契约。

DefaultContractResolver

DefaultContractResolver是Json.NET序列化器所使用的默认解析器。它以可重写的虚拟方法的形式提供了很多扩展扩展途径。

CamelCasePropertyNamesContractResolver

CamelCasePropertyNamesContractResolver继承自DefaultContractResolver,简单重写了JSON的属性名称,改成驼峰大小写的形式。

ContractResolver

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"
//  ]
//}

自定义IContractResolver的示例

使用JsonConverter配合IContractResolver

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;
  }
}

此示例使用一个IContract解析器为一个对象设置了JsonConverter。此处使用一个契约解析器是很有用的,因为DateTime不是你自己的类型,不可能在它上面放一个JsonConvertAttribute。

带有IContractResolver的条件属性

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;
  }
}

此示例利用IContractResolver设置了针对属性的有条件序列化。如果你想要有条件地序列化一个属性,但是不想对你的类型添加额外的方法,IContractResolver就很有用。

带序列化跟踪的调试

Json.NET支持利用ITraceWriter实现记录日志和调试。通过分配跟踪写入程序,您可以捕获序列化消息和错误,并在序列化JSON和反序列化JSON时调试Json.NET序列化器内部发生的情况。

  • ITraceWriter
  • Custom ITraceWriter

ITraceWriter

一个跟踪写入程序,可以利用JsonSerializerSettings或JsonSerializer上的属性来分配它。

利用MemoryTraceWriter调试序列化

Staff staff = new Staff();
staff.Name = "Arnie Admin";
staff.Roles = new List { "Administrator" };
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中。

Custom ITraceWriter

若要用你已有的日志框架写消息,只需要实现一个自定义版本的ITraceWriter。

自定义NLog TraceWriter

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;
      }
  }
}

LINQ to JSON

LINQ to JSON是用来处理JSON对象的API。它的设计考虑了LINQ,以支持快速查询和创建JSON对象。LINQ to JSON位于Newtonsoft.Json.Linq命名空间下面。

使用LINQ for JSON

JObject o = JObject.Parse(@"{
  'CPU': 'Intel',
  'Drives': [
  'DVD read/writer',
  '500 gigabyte hard drive'
  ]
}");
string cpu = (string)o["CPU"];
// Intel
string firstDrive = (string)o["Drives"][0];
// DVD read/writer
IList allDrives = o["Drives"].Select(t => (string)t).ToList();
// DVD read/writer
// 500 gigabyte hard drive

主题

从下面选择一个主题以进一步了解

  • 解析JSON
  • 创建JSON
  • 利用LINQ解析JSON
  • 利用SelectToken查询JSON

解析JSON

LINQ to JSON具有一些方法,可用来从字符串解析JSON,或直接从文件载入JSON。

  • 解析JSON文本
  • 从文件载入JSON

解析JSON文本

可利用Parse(String)从一个字符串读取JSON值。

从文本解析一个JSON对象

string json = @"{
  CPU: 'Intel',
  Drives: [
  'DVD read/writer',
  '500 gigabyte hard drive'
  ]
}";
JObject o = JObject.Parse(json);

从文本解析一个JSON数组

string json = @"[
  'Small',
  'Medium',
  'Large'
]";
JArray a = JArray.Parse(json);

从文件中载入JSON

还可以利用ReadFrom(JsonReader)直接从文件中载入JSON。

从文件中读取JSON

using (StreamReader reader = File.OpenText(@"c:\person.json"))
{
  JObject o = (JObject)JToken.ReadFrom(new JsonTextReader(reader));
  // do stuff
}

创建JSON

除了解析已有的JSON字符串,还可以从零开始LINQ to JSON对象,创建也一个新的JSON结构。

  • 手工创建JSON
  • 利用LINQ创建JSON
  • 从对象创建JSON

手工创建JSON

逐个设置值,并创建一个对象和数组,给你完全的控制,但是它比其他选项现详细。

创建JSON

JArray array = new JArray();
JValue text = new JValue("Manual text");
JValue date = new JValue(new DateTime(2000, 5, 23));
array.Add(text);
array.Add(date);
string json = array.ToString();
// [
//   "Manual text",
//   "2000-05-23T00:00:00"
// ]

利用LINQ创建JSON

使用LINQ声明式地创建JSON,是一种根据值的集合创建JSON的快速方法。

声明式地创建JSON

List posts = GetPosts();
JObject rss =
  new JObject(
      new JProperty("channel",
          new JObject(
              new JProperty("title", "James Newton-King"),
              new JProperty("link", "http://james.newtonking.com"),
              new JProperty("description", "James Newton-King's blog."),
              new JProperty("item",
                  new JArray(
                      from p in posts
                      orderby p.Title
                      select new JObject(
                          new JProperty("title", p.Title),
                          new JProperty("description", p.Description),
                          new JProperty("link", p.Link),
                          new JProperty("category",
                              new JArray(
                                  from c in p.Categories
                                  select new JValue(c)))))))));
Console.WriteLine(rss.ToString());
//{
//  "channel": {
//    "title": "James Newton-King",
//    "link": "http://james.newtonking.com",
//    "description": "James Newton-King\'s blog.",
//    "item": [
//      {
//        "title": "Json.NET 1.3 + New license + Now on CodePlex",
//        "description": "Announcing the release of Json.NET 1.3, the MIT license and being available on CodePlex",
//        "link": "http://james.newtonking.com/projects/json-net.aspx",
//        "category": [
//          "Json.NET",
//          "CodePlex"
//        ]
//      },
//      {
//        "title": "LINQ to JSON beta",
//        "description": "Announcing LINQ to JSON",
//        "link": "http://james.newtonking.com/projects/json-net.aspx",
//        "category": [
//          "Json.NET",
//          "LINQ"
//        ]
//      }
//    ]
//  }
//}

从对象创建JSON

从非JSON类型创建JSON对象的最后一个选项是使用FromObject()方法。在内部,FromObject将使用JsonSerializer将对象序列化为LINQ to JSON对象,而不是文本。

下面的示例演示了从匿名对象创建一个JSON对象,但是FromObject中可以使用任何.NET对象,来创建JSON。

从对象创建JSON

JObject o = JObject.FromObject(new
{
  channel = new
  {
      title = "James Newton-King",
      link = "http://james.newtonking.com",
      description = "James Newton-King's blog.",
      item =
          from p in posts
          orderby p.Title
          select new
          {
              title = p.Title,
              description = p.Description,
              link = p.Link,
              category = p.Categories
          }
  }
});

配合LINQ查询JSON

LINQ to JSON提供了很多方法,用来从它的对象中读取数据。JObject/JArray上的索引方法让你能够依靠它在对象上的属性名称或集合中的索引来快速取得数据,而Children()让你能够把数据的范围读取为 IEnumerable,然后利用LINQ来查询。

  • 通过属性名称或集合索引来取得值
  • 利用LINQ查询

通过属性名称或集合索引来取得值

从LINQ to JSON取得值的最简单方法是使用JObject/JArray上的Item[Object]索引,然后把返回的JValue强制转换为你想要的类型。

取得JSON值

string json = @"{
  'channel': {
  'title': 'James Newton-King',
  'link': 'http://james.newtonking.com',
  'description': 'James Newton-King\'s blog.',
  'item': [
    {
      'title': 'Json.NET 1.3 + New license + Now on CodePlex',
      'description': 'Announcing the release of Json.NET 1.3, the MIT license and the source on CodePlex',
      'link': 'http://james.newtonking.com/projects/json-net.aspx',
      'categories': [
        'Json.NET',
        'CodePlex'
      ]
    },
    {
      'title': 'LINQ to JSON beta',
      'description': 'Announcing LINQ to JSON',
      'link': 'http://james.newtonking.com/projects/json-net.aspx',
      'categories': [
        'Json.NET',
        'LINQ'
      ]
    }
  ]
  }
}";
JObject rss = JObject.Parse(json);
string rssTitle = (string)rss["channel"]["title"];
// James Newton-King
string itemTitle = (string)rss["channel"]["item"][0]["title"];
// Json.NET 1.3 + New license + Now on CodePlex
JArray categories = (JArray)rss["channel"]["item"][0]["categories"];
// ["Json.NET", "CodePlex"]
IList categoriesText = categories.Select(c => (string)c).ToList();
// Json.NET
// CodePlex

利用LINQ查询

还可以利用LINQ查询JObject/JArray。Children()将JObject/JArray的子元素值返回为一个IEnumerable,然后可以利用标准的WHERE/OrderBy/Select LINQ运算符来查询了。

Note
Children()返回口令的所有子元素。如果它是一个JObject,则将返回一个要处理的属性的集合,如果它是JArray,你将取得数组值的集合。

查询JSON

var postTitles =
  from p in rss["channel"]["item"]
  select (string)p["title"];
foreach (var item in postTitles)
{
  Console.WriteLine(item);
}
//LINQ to JSON beta
//Json.NET 1.3 + New license + Now on CodePlex
var categories =
  from c in rss["channel"]["item"].SelectMany(i => i["categories"]).Values()
  group c by c
  into g
  orderby g.Count() descending
  select new { Category = g.Key, Count = g.Count() };
foreach (var c in categories)
{
  Console.WriteLine(c.Category + " - Count: " + c.Count);
}
//Json.NET - Count: 2
//LINQ - Count: 1
//CodePlex - Count: 1

LINQ to JSON还可以用来手工把JSON转换为.NET对象。

利用LINQ对象反序列化

public class Shortie
{
  public string Original { get; set; }
  public string Shortened { get; set; }
  public string Short { get; set; }
  public ShortieException Error { get; set; }
}
public class ShortieException
{
  public int Code { get; set; }
  public string ErrorMessage { get; set; }
}

当你在操作JSON时,如果它并不严密匹配你的.NET对象,则在.NET对象之间手工序列化和反序列化是很有用的。

利用LINQ反序列化的示例

string jsonText = @"{
  'short': {
  'original': 'http://www.foo.com/',
  'short': 'krehqk',
  'error': {
    'code': 0,
    'msg': 'No action taken'
  }
  }
}";
JObject json = JObject.Parse(jsonText);
Shortie shortie = new Shortie
{
  Original = (string)json["short"]["original"],
  Short = (string)json["short"]["short"],
  Error = new ShortieException
  {
      Code = (int)json["short"]["error"]["code"],
      ErrorMessage = (string)json["short"]["error"]["msg"]
  }
};
Console.WriteLine(shortie.Original);
// http://www.foo.com/
Console.WriteLine(shortie.Error.ErrorMessage);
// No action taken

利用SelectToken查询JSON

SelectToken()提供了利用单个字符串路径到想要的JToken来查询LINQ to JSON的方法。SelectToken使动态查询变得很容易,因为整个查询是定义在字符串中的。

  • SelectToken
  • SelectToken with JSONPath
  • SelectToken with LINQ

SelectToken

SelectToken是JToken上的一种方法,取用一个到子口令的字符串路径。SelectToken返回该子口令,或者返回null引用,如果在那个路径的位置没有找到口令的话。

路径是由属性的名称、数组的索引构成,由点号隔开,例如,Manufacturers[0].Name。

SelectToken示例

JObject o = JObject.Parse(@"{
  'Stores': [
  'Lambton Quay',
  'Willis Street'
  ],
  'Manufacturers': [
  {
    'Name': 'Acme Co',
    'Products': [
      {
        'Name': 'Anvil',
        'Price': 50
      }
    ]
  },
  {
    'Name': 'Contoso',
    'Products': [
      {
        'Name': 'Elbow Grease',
        'Price': 99.95
      },
      {
        'Name': 'Headlight Fluid',
        'Price': 4
      }
    ]
  }
  ]
}");
string name = (string)o.SelectToken("Manufacturers[0].Name");
// Acme Co
decimal productPrice = (decimal)o.SelectToken("Manufacturers[0].Products[0].Price");
// 50
string productName = (string)o.SelectToken("Manufacturers[1].Products[0].Name");
// Elbow Grease

SelectToken配合JSONPath

SelectToken支持JSONPath查询。在此处进一步了解JSONPath。

SelectToken配合JSONPath

JObject o = JObject.Parse(@"{
  'Stores': [
  'Lambton Quay',
  'Willis Street'
  ],
  'Manufacturers': [
  {
    'Name': 'Acme Co',
    'Products': [
      {
        'Name': 'Anvil',
        'Price': 50
      }
    ]
  },
  {
    'Name': 'Contoso',
    'Products': [
      {
        'Name': 'Elbow Grease',
        'Price': 99.95
      },
      {
        'Name': 'Headlight Fluid',
        'Price': 4
      }
    ]
  }
  ]
}");
// manufacturer with the name 'Acme Co'
JToken acme = o.SelectToken("$.Manufacturers[?(@.Name == 'Acme Co')]");
Console.WriteLine(acme);
// { "Name": "Acme Co", Products: [{ "Name": "Anvil", "Price": 50 }] }
// name of all products priced 50 and above
IEnumerable pricyProducts = o.SelectTokens("$..Products[?(@.Price >= 50)].Name");
foreach (JToken item in pricyProducts)
{
  Console.WriteLine(item);
}
// Anvil
// Elbow Grease

SelectToken配合LINQ

SelectToken可以与标准LINQ方法结合使用。

SelectToken配合LINQ的示例

IList storeNames = o.SelectToken("Stores").Select(s => (string)s).ToList();
// Lambton Quay
// Willis Street
IList firstProductNames = o["Manufacturers"].Select(m => (string)m.SelectToken("Products[1].Name")).ToList();
// null
// Headlight Fluid
decimal totalPrice = o["Manufacturers"].Sum(m => (decimal)m.SelectToken("Products[0].Price"));
// 149.95

性能贴士

开箱即用的Json.NET比DataContractJsonSerializer和JavaScriptSerializer更快。此处有一些贴士,让它变得更快

  • 重用契约解析器
  • 优化内存使用
  • JsonConverters
  • 手工序列化
  • 基准

重用契约解析器

IContractResolver在把.NET类型解析为序列化过程中在JsonSerializer内部使用的契约。创建契约涉及到检查反射慢的类型,因此协定通常由IContractResolver的实现(例如DefaultContractResolver)来缓存。

为了避免每次使用JsonSerializer时重新创建契约的开销,你应该创建契约解析器并重用它。注意,如果你没有使用契约解析器,则在序列化和反序列化时,自动使用共享的内部实例。

重用ContractResolver

// BAD - a new contract resolver is created each time, forcing slow reflection to be used
string json1 = JsonConvert.SerializeObject(person, new JsonSerializerSettings
{
  Formatting = Formatting.Indented,
  ContractResolver = new DefaultContractResolver
  {
      NamingStrategy = new SnakeCaseNamingStrategy()
  }
});
// GOOD - reuse the contract resolver from a shared location
string json2 = JsonConvert.SerializeObject(person, new JsonSerializerSettings
{
  Formatting = Formatting.Indented,
  ContractResolver = AppSettings.SnakeCaseContractResolver
});
// GOOD - an internal contract resolver is used
string json3 = JsonConvert.SerializeObject(person, new JsonSerializerSettings
{
  Formatting = Formatting.Indented
});

优化内存使用

为了使应用程序始终保持快速,必须尽量减少.NET框架执行垃圾收集所花费的时间。在垃圾收集过程中,给太多对象分配地址或给非常大的对象分配地址可能会拖慢甚至停止应用程序。

为了最小化内存的使用、分配地址的对象的数目,Json.NET支持对流直接序列化和反序列化。当操作大于85KB的JSON文档时,每次读或写JSON一小片,而不是把整个JSON加载到内存中,是十分重要的,从而避免JSON字符串以large object heap告终。

反序列化字符串

HttpClient client = new HttpClient();
// read the json into a string
// string could potentially be very large and cause memory problems
string json = client.GetStringAsync("http://www.test.com/large.json").Result;
Person p = JsonConvert.DeserializeObject(json);

反序列化流

HttpClient client = new HttpClient();
using (Stream s = client.GetStreamAsync("http://www.test.com/large.json").Result)
using (StreamReader sr = new StreamReader(s))
using (JsonReader reader = new JsonTextReader(sr))
{
  JsonSerializer serializer = new JsonSerializer();
  // read the json from a stream
  // json size doesn't matter because only a small piece is read at a time from the HTTP request
  Person p = serializer.Deserialize(reader);
}

JsonConverters

向SerializeObject或DeserializeObject传入JsonConverter提供了一个简单的方法,来完全改变如何序列化对象的方式。然而,存在一些开销,针对每一个值调用CanConvert方法,以检查是否需要由JsonConverter来处理处理序列化。

存在两种方法可以继续使用JsonConverter,而不需要任何开销。最简单的方法是利用JsonConverterAttribute指定JsonConverter。这个特性告诉序列化器,在序列化和反序列化此类型时始终使用转换器,而不需要检查。

使用JsonConverter配合JsonConverterAttribute

[JsonConverter(typeof(PersonConverter))]
public class Person
{
  public Person()
  {
      Likes = new List();
  }
  public string Name { get; set; }
  public IList Likes { get; private set; }
}

如果你想要转换到的类不是你自己的类,你也不能够使用一个特性,则依然可以使用JsonConverter,只要创建你自己的IContractResolver。

使用JsonConverter配合IContractResolver

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将把所有的DateTime设置为使用JavaScriptDateConverter。

手工序列化

读取和写出JSON的绝对最快的方式是使用JsonTextReader/JsonTextWriter来直接手工序列化类型。使用reader或writer直接跳过来自序列化器的任何开销,譬如反射。

使用JsonTextWriter手工序列化

public static string ToJson(this Person p)
{
  StringWriter sw = new StringWriter();
  JsonTextWriter writer = new JsonTextWriter(sw);
  // {
  writer.WriteStartObject();
  // "name" : "Jerry"
  writer.WritePropertyName("name");
  writer.WriteValue(p.Name);
  // "likes": ["Comedy", "Superman"]
  writer.WritePropertyName("likes");
  writer.WriteStartArray();
  foreach (string like in p.Likes)
  {
      writer.WriteValue(like);
  }
  writer.WriteEndArray();
  // }
  writer.WriteEndObject();
  return sw.ToString();
}

如果性能很重要,你不介意多写代码来实现它,则这是你的最佳选择。你可以在此处进一步了解如何使用JsonReader/JsonWriter:JSON的读写基础

利用JSON模式校验JSON

Json.NET通过JsonSchema类和 JsonValidatingReader类来支持JSON模式标准。它位于Newtonsoft.Json.Schema命名空间中。

JSON模式用来校验JSON片段的结构和数据类型,类似于XML模式对于XML。你可以在此处读取JSON模式:json-schema.org

Note
过时! JSON模式校验已经被移到它自己的包里了。请参阅https://www.newtonsoft.com/jsonschema以了解细节。

利用JSON模式校验JSON

检查JSON是否有效的最简单方法是把JSON加载到一个JObject或JArray中,然后使用IsValid(JToken, JsonSchema)方法配合JSON模式。

利用IsValid校验JSON

string schemaJson = @"{
  'description': 'A person',
  'type': 'object',
  'properties':
  {
  'name': {'type':'string'},
  'hobbies': {
    'type': 'array',
    'items': {'type':'string'}
  }
  }
}";
JsonSchema schema = JsonSchema.Parse(schemaJson);
JObject person = JObject.Parse(@"{
  'name': 'James',
  'hobbies': ['.NET', 'Blogging', 'Reading', 'Xbox', 'LOLCATS']
}");
bool valid = person.IsValid(schema);
// true

若要获得校验错误消息,请使用IsValid(JToken, JsonSchema, IList )重载或Validate(JToken, JsonSchema, ValidationEventHandler)重载。

利用IsValid校验JSON

JsonSchema schema = JsonSchema.Parse(schemaJson);
JObject person = JObject.Parse(@"{
  'name': null,
  'hobbies': ['Invalid content', 0.123456789]
}");
IList messages;
bool valid = person.IsValid(schema, out messages);
// false
// Invalid type. Expected String but got Null. Line 2, position 21.
// Invalid type. Expected String but got Float. Line 3, position 51.

IsValid内在地使用JsonValidatingReader来实施JSON模式校验。若要跳过把JSON载入到JObject/JArray中的开销,请校验JSON,然后把JSON反序列化为类,可以在反序列化对象的时候,使用JsonValidatingReader配合JsonSerializer来校验JSON。

利用JsonValidatingReader校验JSON

string json = @"{
  'name': 'James',
  'hobbies': ['.NET', 'Blogging', 'Reading', 'Xbox', 'LOLCATS']
}";
JsonTextReader reader = new JsonTextReader(new StringReader(json));
JsonValidatingReader validatingReader = new JsonValidatingReader(reader);
validatingReader.Schema = JsonSchema.Parse(schemaJson);
IList messages = new List();
validatingReader.ValidationEventHandler += (o, a) => messages.Add(a.Message);
JsonSerializer serializer = new JsonSerializer();
Person p = serializer.Deserialize(validatingReader);

创建JSON模式

获得JsonSchema对象的最简单方法是从一个字符串或文件中载入它。

从字符串或文件创建JSON模式

// load from a string
JsonSchema schema1 = JsonSchema.Parse(@"{'type':'object'}");
// load from a file
using (TextReader reader = File.OpenText(@"c:\schema\Person.json"))
{
  JsonSchema schema2 = JsonSchema.Read(new JsonTextReader(reader));
  // do stuff
}

也可以在代码中创建JsonSchema对象。

在代码是创建新的JSON

JsonSchema schema = new JsonSchema();
schema.Type = JsonSchemaType.Object;
schema.Properties = new Dictionary
{
  { "name", new JsonSchema { Type = JsonSchemaType.String } },
  {
      "hobbies", new JsonSchema
      {
          Type = JsonSchemaType.Array,
          Items = new List { new JsonSchema { Type = JsonSchemaType.String } }
      }
  },
};
JObject person = JObject.Parse(@"{
  'name': 'James',
  'hobbies': ['.NET', 'Blogging', 'Reading', 'Xbox', 'LOLCATS']
}");
bool valid = person.IsValid(schema);
// true

你可能感兴趣的:(整理)