JavaScript 和 .NET 中的 JavaScript 对象表示法 (JSON) 简介

 

JavaScript 和 .NET 中的 JavaScript 对象表示法 (JSON) 简介

阿蒂夫·阿齐兹,斯科特·米切尔

2007 年 2 月

适用于:
   JSON
   Ajax

总结: 本文讨论 JavaScript 对象表示法 (或 JSON) ,这是一种开放和基于文本的数据交换格式,可提供更适合 Ajax 样式 Web 应用程序的标准化数据交换格式。 ) (22 个打印页

目录

简介
了解 JavaScript 中的文本表示法
将 JSON 与 XML 进行比较
使用 JavaScript 创建和分析 JSON 消息
在.NET Framework中使用 JSON
结论
参考

下载本文的源代码

简介

设计将与远程计算机通信的应用程序时,必须选择数据格式和交换协议。 有多种开放、标准化的选项,理想的选择取决于应用程序要求和预先存在的功能。 例如,基于 SOAP 的 Web 服务在包装在 SOAP 信封中的 XML 有效负载中格式化数据。

虽然 XML 适用于许多应用程序方案,但它有一些缺点,使得它不太适合其他方案。 XML 通常不太理想的一个空间是 Ajax 样式的 Web 应用程序。 Ajax 是一种用于构建交互式 Web 应用程序的技术,通过使用带外轻量级调用 Web 服务器来代替整页回发,从而提供更快速的用户体验。 这些异步调用使用 JavaScript 在客户端上启动,涉及格式化数据、将其发送到 Web 服务器,以及分析和处理返回的数据。 虽然大多数浏览器可以构造、发送和分析 XML,但 JavaScript 对象表示法 (或 JSON) 提供了更适合 Ajax 样式 Web 应用程序的标准化数据交换格式。

JSON 是一种基于文本的开放数据交换格式, (请参阅 RFC 4627) 。 与 XML 一样,它是可读的、独立于平台的,并且具有广泛的实现可用性。 根据 JSON 标准格式化的数据是轻量级的,可通过 JavaScript 实现轻松分析,使其成为 Ajax Web 应用程序的理想数据交换格式。 由于 JSON 主要是一种数据格式,因此它不仅限于 Ajax Web 应用程序,并且几乎可用于应用程序需要交换或存储结构化信息作为文本的任何方案。

本文介绍 JSON 标准、它与 JavaScript 的关系,以及它与 XML 的比较情况。 讨论 Jayrock 是用于 .NET 的开源 JSON 实现,并在 JavaScript 和 C# 中提供了创建和分析 JSON 消息的示例。

了解 JavaScript 中的文本表示法

文本在编程语言中用于从 字面上 表示固定值,例如常量整数值 4 或字符串“Hello, World”。文本可用于大多数语言(如控件语句中的条件的一部分、调用函数时的输入参数、变量赋值等)中的表达式。 例如,以下 C# 和 Visual Basic 代码使用常量整数值 42 初始化变量 x

  
    int x = 42;  // C#
Dim x As Integer = 42  ' Visual Basic
  

不同的编程语言允许使用不同类型的文本。 大多数编程语言至少支持标量类型(如整数、浮点数、字符串和布尔值)的文本。 JavaScript 的有趣之处在于,除了标量类型外,它还支持结构化类型(如数组和对象)的文本。 此功能允许使用简洁的语法按需创建和初始化数组和对象。

JavaScript 中的数组文本由零个或多个表达式组成,每个表达式表示数组的一个元素。 数组元素用方括号括起来, ([]) 用逗号分隔。 以下示例从 字面上 定义了一个数组,其中包含七个大洲的名称的字符串元素:

  
    var continents = ["Europe", "Asia", "Australia", "Antarctica", "North
 America", "South America", "Africa"];
alert(continents[0] + " is one of the " + continents.length + "
 continents.");
  

现在比较一下如何在 JavaScript 中创建和初始化数组,而不使用文本表示法:

  
    var continents = new Array();
continents[0] = "Europe";
continents[1] = "Asia";
continents[2] = "Australia";
continents[3] = "Antarctica";
continents[4] = "North America";
continents[5] = "South America";
continents[6] = "Africa";
  

对象文本定义对象的成员及其值。 对象成员和值的列表用大括号 ({}) 括起来,每个成员用逗号分隔。 在每个成员中,名称和值由冒号 (分隔:) 。 以下示例创建一个 对象,并使用三个名为 AddressCityPostalCode 的成员对其进行初始化,其值分别为“123 Anywhere St.”、“Springfield”和“99999”。

  
    var mailingAddress = { 
     "Address"    :   "123 Anywhere St.", 
     "City"       :   "Springfield", 
     "PostalCode" :   99999
};
alert("The package will be shipped to postal code " +
 mailingAddress.PostalCode);
  

到目前为止提供的示例演示了在数组和对象文本中使用字符串和数字文本。 还可以通过递归方式使用表示法来表示整个图形,以便数组元素和对象成员值本身可以使用对象和数组文本。 例如,以下代码片段演示了一个对象,该对象将数组作为 PhoneNumbers) 成员 (,其中数组由对象列表组成。

  
    var contact = {
     "Name": "John Doe",
     "PermissionToCall": true,
     "PhoneNumbers": [ 
       {
           "Location": "Home",
           "Number": "555-555-1234"
       },
       {
           "Location": "Work",
           "Number": "555-555-9999 Ext. 123"
       }
     ]
};
if (contact.PermissionToCall)
{
  alert("Call " + contact.Name + " at " + contact.PhoneNumbers[0].Number);
}
  

注意 有关 JavaScript 文本支持的更全面讨论,请参阅 Core JavaScript 1.5 指南“文本”部分

从 JavaScript 文本到 JSON

JSON 是从 JavaScript 中文本对象表示法的子集创建的一种数据交换格式。 虽然 JavaScript 对文本值接受的语法非常灵活,但请务必注意,JSON 的规则要严格得多。 例如,根据 JSON 标准,对象成员的名称 必须是 有效的 JSON 字符串。 JSON 中的字符串 必须 用引号引起来。 另一方面,JavaScript 允许用引号或撇号分隔对象成员名称,或者完全省略引用,只要成员名称不与保留的 JavaScript 关键字 (keyword) 冲突。 同样,JSON 中的数组元素或对象成员值限制为非常有限的集。 但是,在 JavaScript 中,数组元素和对象成员值可以引用几乎任何有效的 JavaScript 表达式,包括函数调用和定义!

JSON 的魅力在于它的简单性。 根据 JSON 标准设置格式的消息由单个顶级对象或数组组成。 数组元素和对象值可以是对象、数组、字符串、数字、布尔值 (true 和 false) 或 null。 简而言之,这是 JSON 标准! 真的就这么简单。 有关 标准的 更正式说明,请参阅 www.json.org 或 RFC 4627

JSON 的一个痛点是缺少日期/时间文本。 许多人在首次遇到 JSON 时对了解这一点感到惊讶和失望。 (安慰或不) 缺少日期/时间文本的简单解释是 JavaScript 从未有过:JavaScript 中对日期和时间值的支持完全通过 Date 对象提供。 因此,大多数使用 JSON 作为数据格式的应用程序通常倾向于使用字符串或数字来表示日期和时间值。 如果使用字符串,通常可以采用 ISO 8601 格式。 如果使用数字,则值通常采用表示自 epoch 以来世界协调时 (UTC) 的毫秒数,其中 epoch 定义为 1970 年 1 月 1 日午夜 (UTC) 。 同样,这只是一个约定,而不是 JSON 标准的一部分。 如果要与其他应用程序交换数据,则需要检查其文档,以了解它如何在 JSON 文本中对日期和时间值进行编码。 例如,Microsoft 的 ASP.NET AJAX 均未使用上述约定。 相反,它将 .NET DateTime 值编码为 JSON 字符串,其中字符串的内容为 \/Date (刻度) \/ ,而 刻度 表示自 epoch (UTC) 以来的毫秒数。 因此,1989 年 11 月 29 日上午 4:55:30,UTC 编码为“\/Date (628318530718) \/”。 有关这种相当精明的编码选择背后的一些理由,请参阅“内部 ASP.NET AJAX 的 JSON 日期和时间字符串”。

将 JSON 与 XML 进行比较

JSON 和 XML 都可用于以基于文本的、可读的数据交换格式表示本机内存中对象。 此外,这两种数据交换格式是同构的 -- 给定文本采用一种格式,另一种格式是可想而知的。 例如,在调用 Yahoo! 的一个可公开访问的 Web 服务时,可以通过查询字符串参数指示响应的格式应为 XML 还是 JSON。 因此,在决定数据交换格式时,选择一种格式而不是另一种作为银弹并不简单,而是哪种格式具有使它成为特定应用程序的最佳选择 的特征 。 例如,XML 源于标记文档文本,并且往往在该空间 (非常出色,这与 XHTML) 一样。 另一方面,JSON 源于编程语言类型和结构,因此提供了更自然、更易获得的映射来交换结构化数据。 除了这两个起点之外,下表将帮助你了解和比较 XML 和 JSON 的关键特征。

XML 和 JSON 之间的主要特征差异

特征 XML JSON
数据类型 不提供数据类型的任何概念。 必须依赖 XML 架构 来添加类型信息。 提供标量数据类型,以及通过数组和对象表达结构化数据的功能。
对数组的支持 数组必须通过约定来表示,例如,通过使用外部占位符元素将数组内容建模为内部元素。 通常,外部元素使用用于内部元素的名称的复数形式。 本机数组支持。
对对象的支持 对象必须通过约定来表示,通常通过混合使用属性和元素。 本机对象支持。
Null 支持 要求对 XML 实例文档中的元素使用 xsi:nil ,并导入相应的命名空间。 本机识别 null 值。
注释 本机支持,通常通过 API 提供。 不支持。
命名空间 支持命名空间,这消除了合并文档时名称冲突的风险。 命名空间还允许安全地扩展现有的基于 XML 的标准。 没有命名空间的概念。 通常可以通过嵌套对象或在对象成员名称中使用前缀来避免命名冲突, (在实践中首选前者) 。
格式设置决策 复杂中将基于这些变数介绍更复杂的漫游方案。 需要付出更大的努力来决定如何将应用程序类型映射到 XML 元素和属性。 可以引发激烈的争论,即以元素为中心的方法还是以属性为中心的方法更好。 简单。 为应用程序数据提供更直接的映射。 唯一的例外可能是缺少日期/时间文本。
大小 文档的大小往往很长,尤其是在使用以元素为中心的格式设置方法时。 语法非常简洁,并生成格式化文本,其中大部分空间 (正确使用,因此由表示的数据) 。
在 JavaScript 中分析 需要 XML DOM 实现和其他应用程序代码才能将文本映射回 JavaScript 对象。 分析文本不需要其他应用程序代码;可以使用 JavaScript 的 eval 函数。
学习曲线 通常往往需要协同使用多种技术: XPath、XML 架构、 XSLTXML 命名空间DOM 等。 非常简单的技术堆栈,对具有 JavaScript 或其他动态编程语言背景的开发人员已经很熟悉。

JSON 是一种相对较新的数据交换格式,没有 XML 目前 (多年采用或供应商支持,尽管 JSON 正在迅速赶上) 。 下表突出显示了 XML 和 JSON 空间中的当前状态。

支持 XML 和 JSON 之间的差异

支持 XML JSON
工具 拥有一套成熟的工具,可从许多行业供应商处获得。 丰富的工具支持(如编辑器和格式化程序)很少。
Microsoft .NET Framework 自 .NET Framework 1.0 版起,非常良好且成熟的支持。 XML 支持作为基类库 (BCL) 的一部分提供。 对于非托管环境,有 MSXML。 到目前为止,除了作为 ASP.NET AJAX 的一部分的初始实现之外,没有。
平台和语言 分析和格式化程序在许多平台和语言中广泛使用, (商业和开放源代码实现) 。 分析器和格式化程序已在许多平台和多种语言中可用。 请 查阅 json.org 以获取一组良好的引用。 目前大多数实现往往开放源代码项目。
集成语言 行业供应商目前正在试验语言支持。 有关详细信息 ,请参阅 Microsoft 的 LINQ 项目 仅在 JavaScript/ECMAScript 中本机受支持。

注意 这两个表都不是比较点的综合列表。 两种数据格式都有进一步的比较角度,但我们认为这些关键点应该足以形成初始印象。

使用 JavaScript 创建和分析 JSON 消息

使用 JSON 作为数据交换格式时,有两个常见任务是将本机和内存中表示形式转换为其 JSON 文本表示形式,反之亦然。 遗憾的是,在编写本文时,JavaScript 不提供内置函数来从给定对象或数组创建 JSON 文本。 这些方法预计将在 2007 年包含在 ECMAScript 标准的第四版中。 在将这些 JSON 格式设置函数正式添加到 JavaScript 并在常用实现中广泛使用之前,请使用可在 下载的 http://www.json.org/json.js参考实现脚本。

在撰写本文时的最新迭代中, www.json.org json.js 脚本将 jSONString () 函数添加到数组、字符串、布尔值、对象和其他 JavaScript 类型。 number 和 Boolean) 等标量类型 (的 toJSONString () 函数非常简单,因为它们只需要返回实例值的字符串表示形式。 例如,布尔类型的toJSONString () 函数如果值为 true,则返回字符串“true”,否则返回“false”。 数组和对象类型的 toJSONString () 函数更有趣。 对于数组实例,将按顺序调用每个包含元素的 toJSONString () 函数,结果用逗号连接以分隔每个结果。 括在方括号中的最终输出。 同样,对于 Object 实例,将枚举每个成员,并调用其 toJSONString () 函数。 成员名称和其值的 JSON 表示形式与中间的冒号连接;每个成员名称和值对都用逗号分隔,整个输出用大括号括起来。

toJSONString () 函数的净结果是,任何类型都可以通过单个函数调用转换为其 JSON 格式。 以下 JavaScript 创建一个 Array 对象,并特意使用详细和非文本方法添加七个 String 元素,以便进行说明。 然后,它继续显示数组 JSON 表示形式:

  
    // josn.js must be included prior to this point

var continents = new Array();
continents.push("Europe");
continents.push("Asia");
continents.push("Australia");
continents.push("Antarctica");
continents.push("North America");
continents.push("South America");
continents.push("Africa");

alert("The JSON representation of the continents array is: " +
 continents.toJSONString());
  

Bb299886.intro_to_json01 (en-us,MSDN.10) .gif

图 1. toJSONString () 函数根据 JSON 标准发出格式化的数组。

分析 JSON 文本更简单。 由于 JSON 只是 JavaScript 文本的子集,因此可以使用 eval (expr) 函数, 将源 JSON 文本视为 JavaScript 源代码,将其解析为内存中的表示形式。 eval 函数接受有效 JavaScript 代码字符串作为输入,并计算表达式。 因此,只需以下单行代码即可将 JSON 文本转换为本机表示形式:

  
    var value = eval( "(" + jsonText + ")" );
  

注意 使用额外的括号使 eval 无条件地将源输入视为表达式。 这对于 对象尤其重要。 如果尝试使用包含定义对象的 JSON 文本的字符串(如字符串“”{} (表示空对象) )调用 eval,则它只是返回 undefined 作为分析的结果。 括号强制 JavaScript 分析器将顶级大括号视为 Object 实例的文本表示法,而不是使用大括号来定义语句块。 顺便说一句,如果顶级项是数组,则不会出现相同的问题,如 eval (“[1,2,3]”) 。 但是,为了保持一致,在调用 eval 之前,JSON 文本应始终用括号括起来,以便对如何解释源没有歧义。

计算文本表示法时,将返回与文本语法对应的实例并将其分配给 。 请考虑以下示例,该示例使用 eval 函数分析数组的文本表示法,并将生成的数组分配给变量 大陆

  
    var arrayAsJSONText = '["Europe", "Asia", "Australia", "Antarctica",
 "North America", "South America", "Africa"]';
var continents = eval( arrayAsJSONText );
alert(continents[0] + " is one of the " + continents.length + "
 continents.");
  

当然,在实践中,评估的 JSON 文本将来自某些外部源,而不是像上述情况那样进行硬编码。

eval 函数盲目计算传递的任何表达式。 因此,不受信任的源可能包含潜在危险的 JavaScript 以及构成 JSON 数据的文本表示法或将其混合到文本表示法中。 在源不受信任的情况下,强烈建议使用 parseJSON () 函数分析 JSON 文本, (json.js) :

  
    // Requires json.js
var continents = arrayAsJSONText.parseJSON();
  

parseJSON () 函数也使用 eval,但前提是 arrayAsJSONText 中包含的字符串符合 JSON 文本标准。 它使用巧妙的正则表达式测试来执行此操作。

在.NET Framework中使用 JSON

可以轻松从 JavaScript 代码创建和分析 JSON 文本,这是其吸引力的一部分。 但是,在 ASP.NET Web 应用程序中使用 JSON 时,只有浏览器才享有 JavaScript 支持,因为服务器端代码很可能是使用 Visual Basic 或 C# 编写的。

大多数专为 ASP.NET 设计的 Ajax 库都支持以编程方式创建和分析 JSON 文本。 因此,若要在 .NET 应用程序中使用 JSON,请考虑使用这些库之一。 有许多开源和第三方选项,Microsoft 也拥有自己的名为 ASP.NET AJAX 的 Ajax 库。

本文将介绍使用 Jayrock 的示例,Jayrock 是共同作者 Atif Aziz 创建的 Microsoft .NET Framework JSON 的开源实现。 我们选择使用 Jayrock 而不是 ASP.NET AJAX,原因有三:

  • Jayrock 是开源的,可以根据需要进行扩展或自定义。
  • Jayrock 可用于 ASP.NET 1.x、2.0 和 Mono 应用程序,而 ASP.NET AJAX 仅适用于 ASP.NET 版本 2.0。
  • Jayrock 的范围仅限于 JSON 和 JSON-RPC,而前者是本文main重点。 虽然 ASP.NET AJAX 包含对创建和分析 JSON 文本的一些支持,但它的主要用途是提供一个丰富的平台,用于在 ASP.NET 中构建端到端 Ajax 样式的 Web 应用程序。 当main焦点为 JSON 时,额外的钟声和哨声可能会分散注意力。

使用 Jayrock 在 .NET 中使用 JSON 类似于通过.NET Framework中的 XmlWriterXmlReaderXmlSerializer 类使用 XML。 Jayrock 中找到的 JsonWriterJsonReaderJsonTextWriterJsonTextReader 类模拟.NET Framework类 XmlWriterXmlReaderXmlTextWriterXmlTextReader 的语义。 这些类可用于在低级别和面向流的级别与 JSON 进行交互。 使用这些类,可以通过一系列方法调用逐个创建或分析 JSON 文本。 例如,使用 JsonWriter 类方法 WriteNumber (number) 根据 JSON 标准写出相应的 数字 字符串表示形式。 JsonConvert 类提供 ExportImport 方法,用于在 .NET 类型和 JSON 之间进行转换。 这些方法提供的功能与 XmlSerializer 类方法 SerializeDeserialize 中的功能类似。

创建 JSON 文本

以下代码演示如何使用 JsonTextWriter 类为各大洲的字符串数组创建 JSON 文本。 此 JSON 文本发送到传递到构造函数的 TextWriter 实例,该实例恰好是本示例中控制台的输出流 (ASP.NET 可以使用 Response.Output) :

  
    using (JsonTextWriter writer = JsonTextWriter(Console.Out))
{
    writer.WriteStartArray();
    writer.WriteString("Europe");
    writer.WriteString("Asia");
    writer.WriteString("Australia");
    writer.WriteString("Antarctica");
    writer.WriteString("North America");
    writer.WriteString("South America");
    writer.WriteString("Africa");
    writer.WriteEndArray();
}
  

除了 WriteStartArrayWriteStringWriteEndArray 方法外, JsonWriter 类还提供用于编写其他 JSON 值类型(如 WriteNumberWriteBooleanWriteNull 等)的方法。 WriteStartObjectWriteEndObjectWriteMember 方法为对象创建 JSON 文本。 以下示例演示如何为“了解 JavaScript 中的文本表示法”部分中检查的联系人对象创建 JSON 文本:

private static void WriteContact()
{
    using (JsonWriter w = new JsonTextWriter(Console.Out))
    {
        w.WriteStartObject();              // {
        w.WriteMember("Name");             //   "Name" : 
        w.WriteString("John Doe");         //     "John Doe",
        w.WriteMember("PermissionToCall"); //   "PermissionToCall" :
        w.WriteBoolean(true);              //     true,
        w.WriteMember("PhoneNumbers");     //   "PhoneNumbers" :
        w.WriteStartArray();               //   [ 
        WritePhoneNumber(w,                //     { "Location": "Home",
            "Home"                         //       "Number": 
            "555-555-1234");               //         "555-555-1234" },
        WritePhoneNumber(w,                //     { "Location": "Work",
            "Work",                        //       "Number": 
            "555-555-9999");               //       "555-555-9999" }
        w.WriteEndArray();                 //   ]
        w.WriteEndObject();                // }
    }
}

private static void WritePhoneNumber(JsonWriter w, string location,
    string number)
{
    w.WriteStartObject();      //  {
    w.WriteMember("Location"); //      "Location" : 
    w.WriteString(location);   //          "...", 
    w.WriteMember("Number");   //      "Number" :
    w.WriteString(number);     //          "..."
    w.WriteEndObject();        //  }
}

JsonConvert 类中的 Export和 ExportToString 方法可用于将指定的 .NET 类型序列化为 JSON 文本。 例如,对 JsonConvert.ExportToString 的以下调用将生成相同的结果,而不是使用 JsonTextWriter 类手动生成七大洲数组的 JSON 文本:

string[] continents = {
      "Europe", "Asia", "Australia", "Antarctica", "North America", 
      "South America", "Africa"
};
string jsonText = JsonConvert.ExportToString(continents);

分析 JSON 文本

JsonTextReader 类提供了各种方法,用于分析 JSON 文本的令牌,其核心为 Read。 每次调用 Read 方法时,分析程序都会使用下一个标记,可以是字符串值、数字值、对象成员名称、数组的开头等。 在适用的情况下,可以通过 Text 属性访问当前令牌的已分析文本。 例如,如果读取器位于布尔数据上,则 Text 属性将返回“true”或“false”,具体取决于实际的分析值。

以下示例代码使用 JsonTextReader 类来分析包含七大洲名称的字符串数组的 JSON 文本表示形式。 以字母“A”开头的每个洲都发送到控制台:

  
    string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

using (JsonTextReader reader = new JsonTextReader(new
 StringReader(jsonText)))
{
    while (reader.Read())
    {
        if (reader.TokenClass == JsonTokenClass.String &&
            reader.Text.StartsWith("A"))
        {
            Console.WriteLine(reader.Text);
        }
    }
}
  

注意 Jayrock 中的 JsonTextReader 类是一个相当宽松的 JSON 文本分析程序。 根据 RFC 4627 中规定的规则,它实际上允许的语法比被视为有效的 JSON 文本多得多。 例如, JsonTextReader 类允许单行和多行注释显示在 JSON 文本中,就像在 JavaScript 中预期的那样。 单行注释以斜杠 () // 开头,多行注释以斜杠star (/*) 开头,以star斜杠 (*/) 结束。 单行注释甚至可以以哈希/磅符号 (#) 开头,这在 Unix 样式配置文件中很常见。 在所有情况下,分析程序会完全跳过注释,并且永远不会通过 API 公开注释。 与在 JavaScript 中一样, JsonTextReader 允许 JSON 字符串由撇号 (') 分隔。 分析程序甚至可以在对象或数组元素的最后一个成员之后允许额外的逗号。

即使添加了所有这些内容, JsonTextReader 也是一个一致分析程序! 另一方面,JsonTextWriter 仅生成严格的符合标准的 JSON 文本。 这遵循通常被创造为稳健性主体的内容,该原则指出,“在所做的事情上保持保守:对你从别人接受的东西要自由。

若要将 JSON 文本直接转换为 .NET 对象,请使用 JsonConvert 类导入方法,指定输出类型和 JSON 文本。 以下示例演示将字符串的 JSON 数组转换为 .NET 字符串数组:

string jsonText = @"[""Europe"", ""Asia"", ""Australia"", ""Antarctica"",
 ""North America"", ""South America"", ""Africa""]";

string[] continents = (string[]) JsonConvert.Import(typeof(string[]),
 jsonText);

下面是一个更有趣的转换示例,它采用 RSS XML 源,使用 XmlSerializer 将其反序列化为 .NET 类型,然后使用 JsonConvert 将对象转换为 JSON 文本 (有效地将 XML 中的 RSS 转换为 JSON 文本) :

XmlSerializer serializer = new XmlSerializer(typeof(RichSiteSummary));
RichSiteSummary news;

// Get the MSDN RSS feed and deserialize it...

using (XmlReader reader = XmlReader.Create("https://msdn.microsoft.com/rss.xml"))
    news = (RichSiteSummary) serializer.Deserialize(reader);

// Export the RichSiteSummary object as JSON text, emitting the output to
// Console.Out.

using (JsonTextWriter writer = new JsonTextWriter(Console.Out))
    JsonConvert.Export(news, writer);

注意RichSiteSummary 的定义及其相关类型可以在本文随附的示例中找到。

在 ASP.NET 中使用 JSON

在了解了在 JavaScript 中使用 JSON 以及从使用 Jayrock 的.NET Framework中处理的方法后,是时候转向一个实际示例,说明在何处以及如何应用所有这些知识。 请考虑 ASP.NET 2.0 中的客户端脚本回调功能,该功能简化了从 Web 浏览器到 ASP.NET 页 (或对页面) 上的特定控件进行带外调用的过程。 在典型的回调方案中,浏览器中的客户端脚本会打包,并将数据发送回 Web 服务器,以便通过服务器端方法进行某些处理。 从服务器接收响应数据后,客户端随后使用该数据更新浏览器显示。

注意 有关详细信息,请参阅 ASP.NET 2.0 中的 MSDN 杂志文章脚本回调。

客户端回调方案中的挑战在于客户端和服务器只能来回传送字符串。 因此,在发送之前,要交换的信息必须从本机内存中表示形式转换为字符串,然后在收到时从字符串分析回其本机内存中表示形式。 ASP.NET 2.0 中的客户端脚本回调功能不需要对交换的数据使用特定的字符串格式,也不提供用于在本机内存中和字符串表示形式之间进行转换的任何内置功能:由开发人员根据所选的某种数据交换格式实现转换逻辑。

以下示例演示如何在客户端脚本回调方案中使用 JSON 作为数据交换格式。 具体而言,该示例包含一个 ASP.NET 页,该页使用 Northwind 数据库中的数据在下拉列表中提供类别列表;所选类别中的产品显示在项目符号列表中, (请参阅图 3) 。 每当在客户端更改下拉列表时,将进行回调,传入其单个元素为所选 CategoryID 的数组。

注意 我们将传入一个数组,该数组包含所选 CategoryID 作为其唯一元素 (,而不仅仅是 CategoryID) ,因为 JSON 标准要求任何 JSON 文本都必须具有对象或数组作为其根。 当然,客户端不需要将 JSON 文本传递给服务器,本可以让此示例仅将所选 的 CategoryID 作为字符串传递。 但是,我们希望演示如何在回调的请求和响应消息中发送 JSON 文本。

Page_Load 事件处理程序中的以下代码配置 Categories DropDownList Web 控件,以便在更改该控件时调用 GetProductsForCategory 函数并传递所选下拉列表值。 如果传入的下拉列表值大于零,此函数将启动客户端脚本回调:

  
    // Add client-side onchange event to drop-down list
Categories.Attributes["onchange"] = "Categories_onchange(this);";

// Generate the callback script
string callbackScript = ClientScript.GetCallbackEventReference(
    /* control        */ this, 
    /* argument       */ "'[' + categoryID + ']'", 
    /* clientCallback */ "showProducts", 
    /* context        */ "null");

// Add the Categories_onchange function
ClientScript.RegisterClientScriptBlock(GetType(),
"Categories_onchange", @"
    function Categories_onchange(sender)
    {
        clearResults();

        var categoryID = sender.value;            
        if (categoryID > 0)
        {
            " + callbackScript + @"
        }
    }", true);
  

ClientScriptManager 类中的 GetCallBackEventReference 方法用于生成调用回调的 JavaScript 代码,具有以下签名:

  
    public string GetCallbackEventReference (
    Control control,
    string argument,
    string clientCallback,
    string context,
)
  

参数指定在回调期间从客户端发送到 Web 服务器的数据,clientCallback 参数指定完成回调后要调用的客户端函数的名称, (showProducts) 。 GetCallBackEventReference 方法调用将生成以下 JavaScript 代码,并将其添加到呈现的标记中:

  
    WebForm_DoCallback('__Page','[' + categoryID + 
']',showProducts,null,null,false)
  

“[” + categoryID + “]” 是在回调期间传递给服务器的值, (具有单个元素的数组, categoryID) ,showProducts 是回调返回时执行的 JavaScript 函数。

在服务器端,为响应回调而执行的方法使用 Jayrock 中的 JsonConvert 类来分析传入的 JSON 文本并设置传出 JSON 文本的格式。 具体而言,将检索与所选类别关联的产品名称,并将其作为字符串数组返回。

  
    // Deserialize the JSON text into an array of integers
int[] args = (int[]) JsonConvert.Import(typeof(int[]), eventArgument);

// Read the selected CategoryID from the array
int categoryID = args[0];

// Get products based on categoryID 

  NorthwindDataSet.ProductsRow[] rows = 
Northwind.Categories.FindByCategoryID(categoryID).GetProductsRows();

// Load the names into a string array
string[] productNames = new string[rows.Length];
for (int i = 0; i < rows.Length; i++)
{
    productNames[i] = rows[i].ProductName;
}

// Serialize the string array as JSON text and return it to the client
return JsonConvert.ExportToString(productNames);

注意JsonConvert 类使用两次,一次用于将 eventArgument 中的 JSON 文本转换为整数数组,然后将字符串数组 productNames 转换为 JSON 文本以返回到客户端。 或者,我们本可以使用此处的 JsonReaderJsonWriter 类,但当涉及的数据相对较小且容易映射到现有类型时, JsonConvert 可以很好地完成相同的工作。

从服务器端返回数据时,将调用从 GetCallBackEventReference 方法指定的 JavaScript 函数并传递返回值。 此 JavaScript 方法 showProducts 首先引用 <div> 元素 ProductOutput。 然后,它会分析 JSON 响应,并动态添加一个无序列表,其中包含每个数组元素的列表项。 如果未为所选类别返回任何产品,则会改为显示相应的消息。

function showProducts(arg, context)
{
    // Dump the JSON text response from the server.

    document.forms[0].JSONResponse.value = arg;
    
    // Parse JSON text returned from callback.

    var categoryProducts = eval("(" + arg + ")");

    // Get a reference to the <div> ProductOutput.
    
    var output = document.getElementById("ProductOutput");

    // If no products for category, show message.
    
    if (categoryProducts.length == 0)
    {
        output.appendChild(document.createTextNode(
            "There are no products for this category..."));
    }
    else
    {
        // There are products, display them in an unordered list. 
        
        var ul = document.createElement("ul");
        
        for (var i = 0; i < categoryProducts.length; i++)
        {
            var product = categoryProducts[i];
            var li = document.createElement("li");
            li.appendChild(document.createTextNode(product));
            ul.appendChild(li);
        }
        
        output.appendChild(ul);
    }
}

图 2 演示了事件序列,而图 3 显示了此示例的运行情况:完整代码包含在本文下载中。

Bb299886.intro_to_json02 (en-us,MSDN.10) .gif

图 2:客户端将所选 CategoryID 作为数组中的单个元素发送,服务器返回关联的产品名称数组。

Bb299886.intro_to_json03 (en-us,MSDN.10) .gif

图 3:产品显示在所选类别内的项目符号列表中。

结论

JSON 是基于 JavaScript 编程语言中文本表示法子集的轻型、基于文本的数据交换格式。 它为应用程序数据结构提供简洁的编码,通常用于 JavaScript 实现可用于交换数据的一个或两个应用程序的情况,例如在 Ajax 样式的 Web 应用程序中。 JSON 的吸引力在于其理解、采用和实现的简单性。 对于已经熟悉 JavaScript 或其他编程语言的开发人员来说,JSON 几乎没有学习曲线,并且对 Python 和 Ruby) 等丰富的文本表示法 (提供类似的支持。 只需调用 eval 函数即可在 JavaScript 代码中分析 JSON 文本,并且使用 中 http://www.json.org/json.js提供的json.js脚本轻松创建 JSON 文本。

在所有主要平台和框架中使用 JSON 的库数量日益增多。 本文介绍了 Jayrock,这是一个开源库,用于在 .NET 应用程序中创建和分析 JSON 文本。 Jayrock 可用于 ASP.NET 1.x、2.0 和 Mono 应用程序。 ASP.NET AJAX 提供类似的 JSON 功能,但仅适用于 ASP.NET 2.0 应用程序。

编程快乐!

参考

Ajax 还是 AJAX?

Ajax 一词最初是由 Jesse James Garrett 创造的,用于描述 Web 应用程序的样式和一组涉及创建高度交互式 Web 应用程序的技术。 从历史上看,术语 Ajax 作为首字母缩略词 AJAX 在 Web 中传播,意思是异步 JavaScript 和 XML。 但是,随着时间的推移,人们意识到,AJAX 中的“X”并不十分代表用于在后台与 Web 服务器通信的基础数据格式,因为大多数实现都切换到 JSON 作为更简单、更高效的替代方案。 因此,首字母缩写词通常被停用,而不是使用 AJAJ 之类的替换首字母缩略词,而改用 Ajax 这个词,而不是 AJAX 作为首字母缩略词。

在撰写本文时,期待看到“AJAX”和“Ajax”的混合和广泛使用,以表示一个和相同的东西。 在本文中,我们坚持使用“Ajax the 术语”。但是,提供支持 Ajax 式应用程序的框架的商业产品往往使用首字母缩略词形式来区分类似名称的清洁剂产品,并避免任何潜在的商标或法律纠纷。

ASP.NET AJAX:JSON 日期和时间字符串内部

ASP.NET 中的 AJAX JSON 序列化程序将 DateTime 实例编码为 JSON 字符串。 在其预发行周期中,ASP.NET AJAX 使用“@ticks@”格式,其中刻度表示自 1970 年 1 月 1 日以协调世界时 (UTC) 的毫秒数。 UTC 中的日期和时间(如 1989 年 11 月 29 日上午 4:55:30)将写出为“@62831853071@”。虽然简单明了,但此格式无法区分序列化日期和时间值与看起来像序列化日期但不应反序列化为一个日期的字符串。 因此,ASP.NET AJAX 团队对最终版本进行了更改,以采用“\/Date (刻度) \/”格式解决此问题。

新格式依赖于一个小技巧来减少误解的可能性。 在 JSON 中,字符串中的正斜杠 (/) 字符可以使用反斜杠 (\) 进行转义,即使它并非严格要求也是如此。 利用这一点,ASP.NET AJAX 团队修改了 JavaScriptSerializer,将 DateTime 实例编写为字符串“\/Date (刻度) \/”。 两个正斜杠的转义是表面的,但对 JavaScriptSerializer 意义重大。 根据 JSON 规则, "\/Date (刻度) \/" 在技术上相当于 "/Date (刻度) /" ,但 JavaScriptSerializer 会将前者反序列化为 DateTime ,后者反序列化为 String。 因此,与预发行版中更简单的“@ticks@”格式相比,出现歧义的可能性要小得多。

特别感谢

在将本文提交到 MSDN 之前,我们让许多志愿者帮助校对文章并提供内容、语法和方向的反馈。 审查过程的主要贡献者包括道格拉斯·克罗克福德、埃里克·舍恩霍尔泽和米兰·内戈万。

关于作者

Atif Aziz 是 Skybow AG 的首席顾问,他的主要重点是帮助客户了解和构建 .NET 开发平台上的解决方案。 Atif 通过在会议上发表演讲和为技术出版物撰写文章,定期为 Microsoft 开发人员社区做出贡献。 他是 INETA 发言人和瑞士最大的 .NET 用户组的总裁。 可在 上或通过他的网站http://www.raboof.com联系atif.aziz@skybow.com他。

Scott Mitchell 是六本 ASP/ASP.NET 书籍的作者,4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 可通过或博客联系 mitchell@4guysfromrolla.com 他: http://ScottOnWriting.net