您的位置:首页 > Web前端 > JavaScript

使用 JavaScript 创建和分析 JSON 消息

2007-09-05 15:14 711 查看

使用 JavaScript 创建和分析 JSON 消息

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

在撰写本文时的最新更新中,www.json.org 上的 json.js 脚本将 toJSONString() 函数添加到数组、字符串、布尔值、对象和其他 JavaScript 类型。该标量类型(如数字和布尔值)的 toJSONString() 函数相当简单,因为它们只需返回实例值的字符串表示形式。例如,如果值为 true,Boolean类型的 toJSONString() 函数返回字符串“true”,否则返回“false”。数组和对象类型的 toJSONString() 函数则更有意思。对于 Array 实例,会依次调用每个所包含元素的 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());




图 1. toJSONString() 函数生成根据 JSON 标准设置格式的数组。
分析 JSON 文本甚至更加简单。由于 JSON 只是 JavaScript 文字的子集,因此可以使用将源 JSON 文本视为 JavaScript 源代码的 eval(expr) 函数
来分析内存中表示形式。eval 函数接受输入有效 JavaScript 代码的字符串,并计算表达式结果。因此,下面一行代码是将 JSON 文本转换为本机表示形式所需的全部代码:

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

注意 使用额外的圆括号可使 eval 将来源输入无条件地视为表达式。这对于对象来说尤其重要。如果您尝试使用包含 JSON 文本的字符串调用 eval,其中该文本定义了对象,如字符串“{}”(表示空对象),那么它只会返回未定义作为分析结果。圆括号会强制 JavaScript 分析器将顶层大括号视为 Object 实例的文字表示法,而不是定义语句块的大括号。同样,如果顶层项是一个数组,如 eval("[1,2,3]"),也不会发生同样的问题。然而,出于一致性需要,在调用 eval 之前 JSON 文本应始终用圆括号括起来,以便分析来源时不会产生歧义。
对文字表示法计算结果后,会返回与文字语法对应的实例,并将其赋值给 value。请看下面的例子:它使用 eval 函数分析数组的文字表示法,并将得到的数组赋值给变量 continents

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.js 中)对 JSON 文本进行分析:

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

parseJSON() 函数也使用 eval,但仅限于 arrayAsJSONText 中包含的字符串符合 JSON 文本标准时。它通过一个更智能的正则表达式测试来完成这一操作。

在 .NET Framework 中使用 JSON

JSON 文本可依据 JavaScript 代码轻松地进行创建和分析,这是它的一个吸引人之处。但是,当 JSON 用于 ASP.NET web 应用程序时,只有浏览器受 JavaScript 支持,因为服务器端代码很可能是用 Visual Basic 或 C# 编写的。

大多数为 ASP.NET 设计的 Ajax 库都支持以编程方式创建和分析 JSON 文本。因此,要在 .NET 应用程序中使用 JSON,请考虑使用其中一个库。有众多的开源和第三方方案可供选择,而 Microsoft 也有自己的 Ajax 库,名为 ASP.NET AJAX

在本文中,我们会介绍使用 Jayrock 的示例,它是一种用于 Microsoft .NET Framework 的 JSON 开源实现,由合著者 Atif Aziz 创建。我们选择使用 Jayrock 而非 ASP.NET AJAX 的原因有三个:

Jayrock 是开源的,可以按需要扩展或自定义。

Jayrock 可用于 ASP.NET 1.x,、2.0 和 Mono 应用程序,而 ASP.NET AJAX 仅限于 ASP.NET 2.0 版。

Jayrock 的作用域仅限于 JSON 和 JSON-RPC,前者是本文的重点内容。ASP.NET AJAX 包括对创建和分析 JSON 文本的一些支持,但它的主要用途是提供一个丰富的平台,在 ASP.NET 中构建端到端 Ajax 风格的 web 应用程序。您的重点内容为 JSON 时,额外的点缀性功能反而会让人分神。

在 .NET 中通过 Jayrock 使用 JSON,与在 .NET Framework 中通过 XmlWriterXmlReaderXmlSerializer 类使用 XML 相似。位于 Jayrock 中的类 JsonWriterJsonReaderJsonTextWriterJsonTextReader 模拟 .NET Framework 类 XmlWriterXmlReaderXmlTextWriterXmlTextReader 的语义。这些类在低级别和面向流的级别与 JSON 交互时非常有用。使用这些类,可通过一系列的方法调用逐个创建或分析 JSON 文本。例如,使用 JsonWriter 类方法 WriteNumber(number) 可根据 JSON 标准写出适当的“数字”形式的字符串表示形式。JsonConvert 类提供了用于在 .NET 类型和 JSON 之间进行转换的 ExportImport 方法。这些方法提供了分别与 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 writer = new JsonTextWriter(Console.Out))
{
writer.WriteStartObject();              //  {
writer.WriteMember("Name");             //      "Name" :
writer.WriteString("John Doe");         //          "John Doe",
writer.WriteMember("PermissionToCall"); //      "PermissionToCall"
:
writer.WriteBoolean(true);              //          true,
writer.WriteMember("PhoneNumbers");     //      "PhoneNumbers" :
writer.WriteStartArray();               //          [
WritePhoneNumber(writer,                //            {
"Location": "Home",
"Home", "555-555-1234");            //              "Number":
"555-555-1234" },
WritePhoneNumber(writer,                //            {
"Location": "Work",
"Work", "555-555-9999 Ext. 123");   //              "Number":
"555-555-9999 Ext. 123" }
writer.WriteEndArray();                 //          ]
writer.WriteEndObject();                //  }
}
}
private static void WritePhoneNumber(JsonWriter writer, string location,
string number)
{
writer.WriteStartObject();      //  {
writer.WriteMember("Location"); //      "Location" :
writer.WriteString(location);   //          "...",
writer.WriteMember("Number");   //      "Number" :
writer.WriteString(number);     //          "..."
writer.WriteEndObject();        //  }
}

JsonConvert 类中的 ExportExportToString 方法可用于将指定的 .NET 类型序列化为 JSON 文本。例如,不需要使用 JsonTextWriter 类为七大洲数组手动构建 JSON 文本,以下对 JsonConvert.ExportToString 的调用即可产生相同的结果:

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

分析 JSON 文本

JsonTextReader 类提供了各种分析 JSON 文本令牌的方法,其中核心的一种是 Read。每次调用 Read 方法时,分析器会使用下一个令牌,可能是字符串值、数字值、对象成员名称、数组开头等。可能的话,可以通过 Text 属性访问当前令牌的已分析文本。例如,如果该读取器位于 Boolean 数据中,则 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 文本多得多。例如,就象在 JavaScript 中一样,JsonTextReader 类允许单行和多行注释出现在 JSON 文本内。单行注释以双斜杠 (//) 开头,多行注释以斜杠星号 (/*) 开头,并以星号斜杠 (*/) 结尾。单行注释甚至能以井字号 (#) 开头,这在 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("http://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);
}[/code]
注意 可在本文附带的示例中找到 RichSiteSummary 定义及其相关类型。

在 ASP.NET 中使用 JSON

我们已经介绍了如何在 JavaScript 中、以及通过 Jayrock 在 .NET Framework 中使用 JSON,接下来我们来看一个关于在何处以及如何应用这些知识的实际示例。考虑 ASP.NET 2.0 的客户端脚本回调功能,它可简化 web 浏览器向 ASP.NET 页面(或向页面中的特定控件)发出带外调用的过程。在典型的回调情形中,浏览器中的客户端脚本将数据打包并回送到 web 服务器,由服务器端方法进行某些处理。从服务器收到响应数据后,客户端会用它来更新浏览器显示。

注意 可在《MSDN 杂志》的文章“ASP.NET 2.0 中的脚本回调”中找到更多信息。
客户端回调情形中的难题在于,客户端和服务器只能来回运送一个字符串。因此,待交换的信息必须在发送前从本机内存中的表示形式转换为字符串,然后在 收到后从字符串分析回本机内存中的表示形式。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,
)

argument 参数指定回调期间哪些数据会从客户端发送到 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 显示了此示例的运作过程;完整的代码包括在本文下载中。



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



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

结束语

JSON 是一种基于文本的轻型数据交换格式,以 JavaScript 编程语言中文字表示法的子集为基础。它提供了用于应用程序数据结构的简洁编码,通常用于一种或两种应用程序交换数据都可以使用 JavaScript 实现的情况,如 Ajax 风格的 web 应用程序。JSON 的吸引人之处在于简单易懂、便于采用和实现。JSON 对于已经熟悉 JavaScript 或同样支持丰富文字表示法的其他编程语言(如 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 作为 Asynchronous JavaScript And XML(异步 JavaScript 和 XML)的缩写 AJAX 在 web 上传播。但随着时间的变化,人们意识到 AJAX 中的“X”无法形象地代表与后台 web 服务器进行通信时使用的基础数据格式,因为大多数实现都转向 JSON 作为更简单而有效的替代方案。因此,没有人提出如 AJAJ(有些拗口)之类的替代缩写,该缩写基本上已引退,而倾向于采用“Ajax 术语”而非“AJAX 缩写”。

在撰写本文时,希望看到混合及广泛使用的“AJAX”和“Ajax”具有同一含义。在本文中,我们坚持“Ajax 术语”。但是,如果商业产品要提供启用 Ajax 样式应用程序的框架,则需要使用缩写形式来区分与其名称类似的洗涤剂产品,避免可能的商标或法律争端。

贴自:http://www.cnblogs.com/valens/archive/2007/07/18/821998.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐