JSON(JavaScript Object Notation)是与 Ajax(AsynchronousJavaScript + XML)同时兴起的。使用 JSON 可以方便地传输数据,因为这些数据还可以重新转换为 JavaScript对象,这一特点使得 JSON 极具实用性。不过仍然需要使用自定义的脚本来处理这些对象。JsonML 是 JSON的一个扩展,通过它我们能够使用 JSON 类型标记映射 XML 数据。而通过这一操作,我们还可以基于 JSON 标记方便地创建 XML 或XHTML 对象,并且构建和交换用户界面(UI)元素。本文向大家展示了如何对这个便于使用的工具加以利用。
Ajax 接口最常遇到的问题之一就是客户机浏览器和主机服务器之间的数据交换通常都需要通过某种编码和后处理(或解析)才能将数据流转换为 JavaScript 应用程序中直接可用的数据。
在 Ajax 中这种封装的方法就是 XML。XML 是一个易于使用和理解的全球性标准,不过它同时也存在着一些问题和局限。XML 的首要问题就是编码处理开销太大,并且在大多数浏览器上对 XML 响应的解码过程也同样地耗时。
在 Ajax 应用程序中,请求往往基于表单的内容。响应是服务器以一系列 Java 对象(可以用来显示信息)的形式返回的信息。其执行顺序类似于图 1 表示的内容。
图 1. 典型的 Ajax 处理
![典型的 Ajax 处理](https://i-blog.csdnimg.cn/blog_migrate/7b9e5a62a90675ce96bf24bbd1166bbc.png)
图 1 中的处理存在两个问题:
- 首先,牵涉到 XML 的处理一般都极其复杂。不论是先生成 XML 还是使用服务器中的 XML,其处理都是相当地耗时和冗长,并且经常效率低下。
- 其次,将数据转换为显示格式也相当复杂。无论是使用 DOM 模型手动构建 UI 元素还是提供预先格式化为 XHTML格式的数据,都是如此。后面这种方式会强制服务器提供 UI元素(这一过程本身可能就是一个危险的事情)。而第一种方式只会给处理添加开销,尽管您想让您的处理尽可能的高效。
对于不同的 Web 浏览器和环境,其 DOM 结构和接口也不尽相同,DOM 模型自然也无法避免这种影响。这样无疑会使所加载信息的显示更具复杂性,而这种复杂性是任何人都不想要或需要的。
要解决这个问题有许多解决方案可以选择。解决方案的第一部分是更改数据从 XML 格式转换为 JSON 格式的方式。这并不是本文的重点,但是理解了JSON 之后会更容易掌握 JsonML。解决方案的另一部分就是 JsonML — 使用它能够更加容易地生成信息的实际显示。我们先来看看JSON。
![]() ![]() |
![]()
|
JSON(JavaScript Object Notation)的目标是解决 XML 使用和解析 XML 内容过程中的大多数问题,并能使用 JavaScript 标准的功能交换信息,从而转换成更有用的内部结构。
JSON在本质上仍然是一种文本格式,只是它更符合人类的阅读习惯,并且与我们通常在许多语言(包括Perl、PHP、Python、Java 和Ruby)中创建嵌套数据对象的方式更加一致,也可以很好地协调 JavaScript 对象格式(其本质上只是一种嵌套数据结构)。
比如说,您可能会建模一个 XML 格式的业务列表,如清单 1 所示。
清单 1. XML 地址数据
<business> <name>One on Wharf</name> <address> <street>1 Wharf Road</street> <town>Grantham</town> <county>Lincolnshire</county> </address> <phonenumbers> <phone>01476 123456</phone> <phone>01476 654321</phone> </phonenumbers> </business> |
要在 JavaScript 中解析这个文件需要使用 DOM 模型访问各个组件。比如说,要在 JavaScript 中获得该结构中的电话号码,您可能会使用到清单 2 中的代码。
清单 2. 使用 XML 数据的 Web 应用程序
<html> <head> <script type="text/javascript"> var xmlDoc; function loadXML() { // code for IE if (window.ActiveXObject) { xmlDoc=new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async=false; xmlDoc.load("/json/address.xml"); getaddress(); } // code for Mozilla, Firefox, Opera, etc. else if (document.implementation && document.implementation.createDocument) { xmlDoc=document.implementation.createDocument("","",null); xmlDoc.load("/json/address.xml"); xmlDoc.οnlοad=getaddress; } else { alert('Your browser cannot handle this script'); } } function getaddress() { document.getElementById("business").innerHTML= xmlDoc.getElementsByTagName("name")[0].childNodes[0].nodeValue; document.getElementById("phone").innerHTML= xmlDoc.getElementsByTagName("phone")[0].childNodes[0].nodeValue; document.getElementById("fax").innerHTML= xmlDoc.getElementsByTagName("phone")[1].childNodes[0].nodeValue; } </script> </head> <body οnlοad="loadXML()"> <h1>Address</h1> <p><b>Business</b> <span id="business"></span><br /> <b>Phone:</b> <span id="phone"></span><br /> <b>Fax:</b> <span id="fax"></span> </p> </body> </html> |
清单 2 中的代码只能获得一个地址的信息,它可以访问原始 XML 文档的各个元素,比如使用如下逻辑获得业务名称:xmlDoc.getElementsByTagName("name")[0].childNodes[0].nodeValue
。
这相当地难看!
再看看 JSON 中同样的信息如何表示吧,请看清单 3。
清单 3. JSON 版本的地址数据
{ "business" : { "name" : "One on Wharf", "address" : { "street" : "1 Wharf Road", "town" : "Grantham", "county" : "Lincolnshire", }, "phonenumbers" : [ "01476 123456", "01476 654321", ] } } |
注意,数据的结构简化了很多而且更易于阅读了。JSON 中所使用的标记格式可直接通过 JavaScript 计算出来,而不需解析程序使用 eval()
进行解析,而且使用 eval() 产生的结果是另一个 JavaScript 对象:var addressbookObj = eval('(' + jsontext + ')');
。
注意:理想情况下不应该使用 eval()
,因为它可用于执行任意格式的文本。可以使用许多只解析 JSON 文本的 JSON 解析程序。
只要 JavaScript 中的信息是对象类型,您就可以使用标准的 JavaScript 标记访问该信息。比如说,您可以从已解析为对象的 JSON 数据源中获得相同的姓名和电话号码,如清单 4 所示。
清单 4. 解析为对象的 JSON 数据源
addressbookObj.business.name addressbookObj.business.phonenumbers[0] addressbookObj.business.phonenumbers[1] |
清单 4 中的表示就更简单,更易于使用和访问了。我们可以使用上面的数据结构提供与原 XML 示例相同的信息,如清单 5 所示。
清单 5. 使用 JSON 数据的 Web 应用程序
<html> <head> <script type="text/javascript" src="prototype.js"></script> <script type="text/javascript"> function showaddress(req) { var addressbookObj = eval('(' + req.responseText + ')'); document.getElementById("business").innerHTML = addressbookObj.business.name; document.getElementById("phone").innerHTML = addressbookObj.business.phonenumbers[0]; document.getElementById("fax").innerHTML = addressbookObj.business.phonenumbers[1]; } function getaddress() { new Ajax.Request('address.json', { method : 'get', onSuccess : showaddress, onFailure: function(){ alert('Something went wrong...') }}); } </script> </head> <body οnlοad="getaddress()"> <h1>Address</h1> <p><b>Business:</b> <span id="business"></span><br /> <b>Phone:</b> <span id="phone"></span><br /> <b>Fax:</b> <span id="fax"></span> </p> </body> </html> |
此处,我使用 Prototype 库加载原始 JSON 文件,但是我们可以看到实际的解析和在 JSON 文件中显示信息的过程简化了很多。然而,我们仍然需要使用 DOM 将从 JSON 文件中加载的信息真正填充至输出。
回到原始 JSON 文件,该文件在数据传输方面的开销相对较小。而在大型 Ajax 项目中,XML 的有效载荷开销可以说是相当大,尤其是将它与开销较少的 JavaScript 合并起来真正处理内容的时候。
上面 清单 5中的示例演示了 JSON 标准的简单性。信息的自定义显示是大多数 Ajax 应用程序的关键。在这个例子中,我们使用 DOM 模型将 XML 或JSON 数据的内容嵌入到了 HTML 页面中。然而,在许多 Ajax 应用程序中,您可能会需要构建一些更复杂的用户接口。
![]() ![]() |
![]()
|
JsonML(JSON Markup Language)使用基本的 JSON 数据交换格式机制并将其应用于 XML 的表示,因此我们可以使用类似的经过简化的基于文本的标记来交换 XML 格式的数据。
这听起来有点不合时宜 — 标记格式使我们能对 XML 数据进行建模,是直接使用 XML 的备选方法 — 但是应该考虑到 JSON在结构和可读性方面相对原始 XML 所具有的优势。使用 JsonML 也可以获得许多同样的优势,可读性和数据大小就是最明显的两个例子。
JsonML 主要是作为一种构建 UI 元素的工具。通常,我们采用如下两种方法开发这种基于浏览器的用户接口:
- 在服务器上生成 XHTML,并使用 DOM 对象的
innerHTML
属性将其插入当前页面。 - 使用客户机 DOM 接口手动构建 DOM 结构。
如您所见,这种方法稍微有点不太方便,尤其是对于大型的数据集合,格式的重复属性会造成处理的混乱。此外,该方法仍然存在处理浏览器敏感的 DOM 实现的问题。
JsonML的目的正是为了解决这一问题。它将 JSON 标记的简单性融入于 DOM 中,并将其作为目标格式。这样能获得与上述方法同样的收益;通过JavaScript 客户机可以方便地加载和解析源数据(无需担心使用中的 DOM 实现)。还有,由于 JsonML文档可以描述任何内容,因此我们可以有效地将数据和标记都嵌入到一个单一的文件中。由于无需使用 DOM构建输出,因此也不必使用复杂的解析过程来生成 UI 元素。
如果您还记得,在前面的 图 1 中,通过 Ajax 接口显示恢复信息的典型方法涉及到如下一些过程:请求数据、解析 XML,以及将 XML 转换为浏览器所需的 DOM 结构然后在输出中实际显示出来。
使用 JsonML,只需一个步骤就可通过 DOM 和 JavaScript 将整个 XML 替换为 XHTML。
![]() ![]() |
![]()
|
在查看 JsonML 的示例之前,有必要了解一下 JsonML 的结构。其结构的正式格式如清单 6 所示。
清单 6. JsonML 标记格式
element = '[' tag-name ',' attributes ',' element-list ']' | '[' tag-name ',' attributes ']' | '[' tag-name ',' element-list ']' | '[' tag-name ']' | json-string ; tag-name = json-string ; attributes = '{' attribute-list '}' | '{' '}' ; attribute-list = attribute ',' attribute-list | attribute ; attribute = attribute-name ':' attribute-value ; attribute-name = json-string ; attribute-value = json-string ; element-list = element ',' element-list | element ; |
尽管这看上去有点复杂,不过其基本结构还是有规律可循的。
首先,元素应该为文本格式,因此我们可以创建这样一个无编号的列表元素:[ "ul" ]
。
方括号将元素在逻辑上分组。如果想在元素中添加属性,需使用括号作为分组中的下一个列表元素。比如说,要更改为没有符号的数据样式,可以使用:[ "ul", { style : "list-style-type:none"} ]
。
当然,没有子元素的列表是毫无用处的;可以将子元素添加到这一列表的最后面,如清单 7 所示。
清单 7. 含有子元素的列表
[ "ul", { style : "list-style-type:none"}, [ "li", "First item"], [ "li", "Second item"], [ "li", "Third item"], ]; |
各项的属性都是可选的,因此可以忽略它们,清单 7 中的列表项就没有属性。
![]() ![]() |
![]()
|
上面几个例子都是 JSON 文件,因此我们可以直接就在 JavaScript 应用程序上对它们进行定义。要将内容解析为实际作为 XHTML 显示的对象,我们需要使用 JsonML Web 站点上的 JsonML 库。可以使用 parseJsonML()
方法将 JSON 对象从 JsonML 解析为 XHTML。JsonML 经过解析后,可以马上将它作为可访问的 UI 元素添加至页面中。
比如说,清单 8 展示了一个完整的 Web 页面,每次单击页面上所提供的链接时该页面都会将无序列表插入到预先定义好的容器中。
清单 8. Web 应用程序,显示了一个 JsonML 模板
<html> <head> <script type="text/javascript" src="JsonML.js"></script> <script type="text/javascript"> var listblock = [ "ul", [ "li", "First item"], [ "li", "Second item"], [ "li", "Third item"], ]; function addblock (id,jsonmlblock) { var aselement = listblock.parseJsonML(); var container = document.getElementById(id); container.appendChild(aselement); } </script> </head> <body> <a href"#list" οnclick="addblock('container', listblock);return false;">Add block</a> <div id="container"/> </body> </html> |
图 2 显示了运行中的 Web 页面,单击链接后需要一段时间将无序列表导入页面,然后才显示出这个页面。
图 2. 运行中的 JsonML 示例,相当简单
![运行中的 JsonML 示例,相当简单](https://i-blog.csdnimg.cn/blog_migrate/b967e7c7b6e728be641db68a17948d97.png)
我们逐步分析这个示例。首先看看 JsonML 是如何表示列表项的,参见清单 9 。
清单 9. JsonML 对列表项的表示
var listblock = [ "ul", [ "li", "First item"], [ "li", "Second item"], [ "li", "Third item"], ]; |
它只是一个 JavaScript 对象。用户单击链接之后便会调用 addblock()
函数。这一过程有三个步骤。首先,应用 parseJsonML()
方法:var aselement = jsonmlblock.parseJsonML();
将 JavaScript 对象解析为 XHTML。
第二步,获得目标容器:var container = document.getElementById(id);
。
最后,添加生成的 XHTML 并将其附加到容器中:container.appendChild(aselement);
。
![]() ![]() |
![]()
|
在构建使用 JsonML 的应用程序之前,您可能希望将一些已有的 XML 或 XHTML 结构转换为 JsonML。JsonML 站点(请参阅 参考资料)提供了 XSL 转换方法,可以从 XML 源输出 JsonML。
要使用该转换方法,请下载 JsonML.xsl 文档,然后使用 xsltproc 命令执行转换。以清单 10 中的 XML 结构为例。
清单 10. XML 结构
<table class="maintable"> <tr class="odd"> <th>Situation</th> <th>Result</th> </tr> <tr class="even"> <td><a href="driving.html" title="Driving">Driving</a></td> <td>Busy</td> </tr> ... </table> |
可以使用如下命令将 清单 10 转换为 JsonML:$ xsltproc JsonML.xsl.xml samp.xml
。
输出结构所清单 11 所示。注意,其结构是经过调整格式的,为简短起见,XSL 在输出中移除了新出现的行。不过它们会作为字符串元素保留在实际的输出中。
清单 11. 输出结果
["table", {"class":"maintable"}, "/n", ["tr", {"class":"odd"}, "/n", ["th", "Situation"], "/n", ["th", "Result"], "/n"], "/n", ["tr", {"class":"even"}, "/n", ["td", ["a", {"href":"driving.html", "title":"Driving"}, "Driving"]], "/n", ["td", "Busy"], "/n"], ] |
只要愿意,我们可以使用这一方法将任何 XML 结构转换为 JsonML。
![]() ![]() |
![]()
|
还会遇到另外一种情况,即我们构建好了一个 XML 或 XHTML 块并且需要将其转换为 JsonML。可以使用一个 JavaScript 函数来执行这种转换。该函数可以从 JsonML Web 站点获得。
![]() ![]() |
![]()
|
可以采用多种方法将 JsonML 当作 UI 生成器使用。最简单易行是方法就是在允当 UI 的 JsonML 中创建一个 JavaScript结构。构建好 JavaScript 结构之后,将该结构转换为 XHTML(只需一个操作)并将生成的 XHTML 插入到页面中。比如说,清单12 展示了一个表格单元的 UI 生成器,使用它可以在表格中的某一行中添加黑色单元或白色单元,或者在表格中添加行,并最终输出表格,它是JsonML 源和相应的 XHTML 源。
清单 12. 基于 JsonML 的表格生成器
<html> <head> <script type="text/javascript" src="JsonML.js"></script> <script type="text/javascript"> var blackcell = [ "td", {"style" : "background-color:black"},"CELL"]; var whitecell = [ "td", {"style" : "background-color:white"},"CELL"]; var currentrow = new Array(); var currenttable = new Array(); function onLoad() { initrow(); inittable(); } function initrow() { currentrow = new Array(); currentrow.push("tr"); } function inittable() { currenttable = new Array(); currenttable.push("table"); currenttable.push({"border" : "1"}); } function addrow() { currenttable.push(currentrow); currentrow = new Array(); currentrow.push("tr"); showsource(); } function addcell(color) { if (color == "black") { currentrow.push(blackcell); } else { currentrow.push(whitecell); } } function showsource() { var tablelement = currenttable.parseJsonML(); var container = document.getElementById("viewabletable"); container.removeChild(container.lastChild); container.appendChild(tablelement); var srccontainer = document.getElementById("sourceoutput"); srccontainer.value = currenttable.toSource(); var domcontainer = document.getElementById("domsourceoutput"); domcontainer.value = container.innerHTML; } function showtable() { showsource(); initrow(); inittable(); } </script> </head> <body οnlοad="onLoad()"> <a href"#addrow" οnclick="addrow();return false;">Add Row</a><br/> <a href"#addbcell" οnclick="addcell('black');return false;" >Add Black Cell</a><br/> <a href"#addwcell" οnclick="addcell('white');return false;" >Add White Cell</a><br/> <a href"#showtable" οnclick="showtable();return false;" >Show Table</a><br/> <a href"#showsource" οnclick="showsource();return false;" >Show Source</a><br/> <div id="viewabletable"> </div> <b>JsonML Source</b> <textarea rows="20" cols="120" id="sourceoutput"></textarea><br/> <b>DOM Source</b> <textarea rows="20" cols="120" id="domsourceoutput"></textarea><br/> </body> </html> |
应用程序的操作相当简单。每次单击Add Black/White Cell 按钮时,都会将一个含有 JsonML格式的正确信息的数组(在本例中即具有正确文本和格式的表格单元)添加到组成当前行的数组中。单击 Add Row时,会将与行相关的数组添加到与整个表格相关的数组中。不论哪种情况,都只是在 JsonML 标记中扩展了内部对象的定义。
单击 Show Table 时,会将 JavaScript 结构解析为 XHTML,然后使用 parseJsonML()
方法将原始 JsonML 转换为所呈现的格式并显示出表格。同时还会显示 JsonML 源(通过对 JavaScript 对象使用 toSource()
方法),并通过转储生成的 XHTML 源显示出 XHTML 源。由于我们只处理内部的 JavaScript 结构,因此不必使用 DOM 接口便可以创建 HTML,将 XHTML 插入最终文档的情况例外。
注意,尽管我们是使用静态文本元素构建表格,不过可以在将表格数据插入行之前对其进行修改。JsonML 模板只不过是一些 JavaScript 对象,因此可以使用这样一个简单赋值操作对内容进行更新:blackcell[2] = "Some other content";
。
模板的内容易于更新,这使我们消除了复杂 UI 构建过程中的一个麻烦:即如何将通过 Ajax 连接加载的信息转换为用于显示的 XHTML 文档。
![]() ![]() |
![]()
|
使用上述例子(清单 12)中的方法可以生成具有内部结构的表格。这种方法相当简单,它可用于生成等价的 XHTML。但是如果想要更改输出以符合某种特定的样式,或者想要更改给定的模板以符合适用于当前页面的样式和输出原则,情况又会如何呢?
JsonML 的用途之一是在加载时生成导入页面的 UI 模板,如清单 13 所示。
清单 13. JsonML 在加载时生成 UI 模板
<script type="text/javascript" src="resultstable.js"></script> <script type="text/javascript" src="resultline.js"></script> <script type="text/javascript" src="resultselectionpopup.js"></script> |
下一步需要输出页面中的不同元素,我们加载各对象并使用 parseJsonML
将其转换为 XHTML,然后输出结果。CSS 必须要止步于此了。清单 13 中的机制根本不可能在结果中引入任何类型的信息或数据。
在传统的 Ajax/DOM model 模型中,由于是单独解析各个元素,因此可以作出这些决定,并可以选择于何时何地应用不同的元素。我们无法以同样的方式与 JsonML 解析处理进行交互,不过却可以在解析阶段添加一个回调绑定,将解析各个元素并输出结果。
对于从 JsonML 源生成的每个 XHTML 标记,都会执行绑定函数。由于每一项都是一个标准的 HTML 对象,因此可以使用任何比较和测定方法对模板的输出进行修改。
比如说,清单 14 显示了另一个 JsonML 格式的表格。
清单 14. 表格的 JsonML 模板
var table = ["table",{ "border" : "1" }, ["tr", ["td",{"class" : "highlight-effect"},"Highlighted row"], ], ["tr", ["td",{"class" : "non-highlight"},"Non-Highlighted row"], ], ]; |
解析该代码时会生成一个简单的表格。可以使用 CSS 类定义所需的格式。但是,您可能不会总是需要将行突出显示出来,只有当插入表格的数据比较重要时才会这样做。
为此,您可以编写一个绑定函数,用于修改元素的 highlight-effect 类以变更元素的背景颜色,如清单 15 所示。
清单 15. 在 JsonML 模板中添加绑定函数
function actionbindings(elem) { if (elem.className.indexOf("highlight-effect") >= 0) { elem.style.backgroundColor = "red"; } return elem; } |
要应用该绑定,可以在 parseJsonML()
函数中调用该绑定:var renderedtable = table.parseJsonML(actionbindings);
。
绑定函数可以访问原始 JsonML 文件中的所有格式元素,因此绑定函数可以依据许多属性(包括标记名称、类名和 id)对格式进行检查和修改。我们只需修改元素,然后将元素返回给解析程序,解析程序会将修改后的元素插入 DOM 树。
![]() ![]() |
![]()
|
虽然本文专注于使用 JsonML 作为 XHTML 的解决方案,不过我们也可以使用 JsonML 处理几乎所有的 XML 数据,因为 XHTML 是主 XML 标准的一个子集。
如果需要将静态 XML 片段存储在 JavaScript 应用程序中,那么这个功能就发挥出了它的作用。不过在应用程序与其他客户机交换信息时,要能够将该文档转换成 XML。
尤其是,由于 JsonML 可以在 JavaScript 中作为结构被访问,因此我们可以轻易地更新 XML 结构中的内容,然后将其转换为 XML 文档并发送给服务器。
比如说,我们可以以清单 16 中的一个简单的 XML 结构为例
清单 16. 简单 XML 结构
<name> <firstname>Martin</firstname> <lastname>Brown</lastname> </name> |
在 JsonML 中,其结构如清单 17 所示
清单 17. 简单 JsonML structure
["name", ["firstname", "Martin"], ["lastname", "Brown"], ] |
然后,我们可以更新 JavaScript 对象中的详细内容,如清单 18 所示。
清单 18. 使用 JavaScript 进行更新
address[1][1] = "Sharon"; |
然后使用 parseJsonML()
生成 XML 版本的对象,创建的结果如清单 19 所示。
清单 19. parseJsonML 生成的 XML 版本的对象
<name> <firstname>Sharon</firstname> <lastname>Brown</lastname> </name> |
与使用 DOM 解析器生成 XML 结构或者使用文本处理(使用不当则容易产生错误)相比,上面的处理显然要简单许多。各种基本模板都比较小且可随时访问。
![]() ![]() |
![]()
|
使用 Ajax 应用程序会引入一系列的问题,比如说如何有效地交换数据,以及如何调整该数据的格式以在应用程序中显示出来。根据定义,Ajax应用程序中的信息是动态的,这表示需要构建 XHTML 格式化该信息。手动编写 XHMTL 相当地耗时,而使用 DOM模型也充满了各种问题,因为各浏览器实现之间的 DOM 接口存在着差异。
![]() |
|
JsonML构建于 JSON 的基础之上,它使我们能够使用 JavaScript 标记建立用户接口元素的模型。这样,比起直接生成基于 XHTML 或基于DOM 元素,构建和填充元素的操作变得更加容易,而且更具兼容性。之后,我们可以将 JavaScript 结构转换成为 XHTML,而不需担心DOM 接口及其差异。
本文介绍了 JSON 和 JsonML标准的基本知识,以及使用它们进行更新操作的简单性。文章还例举了大量的示例,演示了如何使用 JsonML 构建 UI元素。您还了解到如何通过解析阶段中的处理和进一步扩展和增强输出 XHTML 方法来扩展 JsonML 的内容。