HTML与XML都是由标准通用标示语言 (Standard Generalized Markup Language,简称SGML) 发展而来的。SGML是一种涵盖范围很广的语言,它可以用来产生复杂资料系统 (或应用) 的制作方式,并且定义了资料描述及显示规则。基本上,HTML是SGML的一个应用;但 XML则是SGML的一个子集,其设计目的是为了提供一个简单、可使网页最佳化的资料呈现方式,因为使用这种方式比直接使用完善但复杂的SGML要来得容易多了。
XML的衍生规格
XML在设计当初就有将Web考虑在内,而且提供了SGML所没有的好处。浏览器可以利用XML搭配HTML来做资料的显示与呈现。而为了支援XML在 Web 上的传递,目前还有两种XML的相关规格被制定出来。
其中一种规格是「延伸连结语言」 (eXtensible Linking Language 简称XLL),这种规格描述的是一种超连结架构,用来同时支援传统的HTML超连结及更进阶的延伸超连结,其中后者可以让单一连结指向多个不同的连结资源。虽然SGML也可以定义超连结机制,不过在SGML的原始规格中并没有提到。
另一种规格是「延伸样式语言」 (eXtensible Stylesheet Language 简称XSL),这种规格可以提供对XML样式表架构的支援,这也是SGML规格中没有涵盖的部份。样式表允许设计者建立样式范本 (例如粗体、斜体等) 或样式组合,当XML文件显示时,即可将样式范本或组合套用到各种XML元件 (Element)上。
简单来说,XML拥有SGML百分之八十的功能,但其复杂度却只有SGML的百分之二十。基本上,拥有一种简单且通用的资料描述方法是十分有用的。举例来说,由于XML拥有中立的资料交换格式,因此许多不同类型的系统都可以存取其资料。举例来说,就我们所知,很多老旧的电脑系统是用各种不同的格式来储存资料的,但是现在已经有一些系统转换程式,可以让很多旧系统可以读取或输出XML格式的资料。所以,在某个旧系统中用XML制成的资料档案可以轻易地输出到其他旧系统上、或发表成 Web 网页、或输出到其它支援XML的应用程式中。也就是说,XML可以当作是一个中介的资料格式,让原本不相容的系统可以互相交换资料。
XML是一种资料
如果HTML是为了「显示」资讯,那么,XML就是为了「描述」资讯。XML是一个可以让资料结构化并描述资料的标准语言,且可以让不同的应用程式所了解。XML最大的威力就是它可以将使用者介面与资料分开。我们来看一个简单的XML文件,以了解 XML 是如何运作的:
<?xml version="1.0"?>
<EMAIL>
<TO>[email protected]</TO>
<FROM>[email protected]</FROM>
<CC>[email protected]</CC>
<SUBJECT>Basic email</SUBJECT>
<BODY>This is an XML-based email.</BODY>
</EMAIL>
这个例子必须注意的重点是,在文件中并没有描述如何显示此份文件。换句话说,就是没有包含样式的资讯 (如粗体、斜体字、缩排、字体大小等)。因为XML文件的原始码只是描述有哪些资料,让读者可以轻易地了解这份文件的内容以及结构。
XML 文件也被称为「自我描述」 (self-describing) 的文件,因为,每一份 XML文件内都会包含一组资料必须遵循的规则。而由于每一组规则都可以重复使用,因此,文件建立者可以很容易地利用相同类别 (class) 文件的新版本。
类别是出自物件导向的程式观念,在物件导向的程式中,类别用于描述一群有相同特性的物件。基于各文件的内容种类,将文件建立类别,可以提供一种很有用的文件分类方法。
有效的与标准的XML文件
XML 最有用的两个特性是:提供文件结构化的能力,及提供资料自我描述的能力。要有这两种能力,文件中必须要有遵循结构化 (structual) 与文法化 (grammatical) 的规则。接下来的两小节,将简单介绍两个XML词汇:「有效的」 (Valid) 和「标准的」 (Well-formed),这两者描述了文件如何遵循用来控制XML文件的结构化与文法化规则。
有效的(Valid)文件
XML文件处理器 (XML processor) 可强制执行XML文件执行文件内的结构和内容规则。这些规则包括标准XML规格所要求的部份,以及针对该文件所特别定义的规则。其中,第二种规则是以「文件型态定义」 (Document Type Definiton,简称DTD) 存在。DTD 和 XML文件中真正的资料部份是分开的,DTD 可以直接包含在XML文件内部;也可以从XML独立出来,成为一个独立的文件。一个有效的XML文件必须严格地遵守这两种规则。本书并不会详细介绍如何撰写及使用 DTD文件,因为本书只针对简单且不需DTD的XML例子来说明。您可以在任何专业的XML参考手册或网站上找到更多撰写DTD文件的资讯,例如XML
in Action这本书 (by William J. Pardi,Microsoft Press 1999) 和 www.w3.org/xml (W3C的XML网站)。正如您等一下看到的,XML文件不是非得要有DTD文件才行。
标准的 (Well-formed) 文件
标准的 (Well-formed) 文件所要遵循的规则远不如有效文件严格。建立一个标准的XML文件其实是非常简单且直觉的。以下列表会条列出标准化的XML文件主要规则。本章稍后也会举一些例子。
- 只有一个根元件 (Root Element,或称为文件元件Document Element)
- 根元件部份不能出现在文件中其它元件的内容中
- 除了根元件之外,文件中其他元件的开始与结束标签必须构成文件中的同一个巢状层级。这意思是说,如果开始标签放在一个元件的资料内容位置上,结束标签也必须放置在同一个元件的资料内容位置。
如果XML文件不遵循这些规则,浏览器将会出现错误讯息。在 Internet Explorer浏览器中,可以利用Script语言透过物件模型 (Object Model) 来侦测错误。本章稍后也会有错误处理 (Error handling) 的详细介绍。
我们来看看一个XML文件的例子:
<?xml version="1.0">
<PLANT>
<COMMON>Columbine</COMMON>
<BOTANICAL>Aquilegia canadensis</BOTANICAL>
</PLANT>
这个文件包含了一个PLANT文件元件 (称为Document Element或根元件),而COMMON和BOTANICAL是包含在根元件内部的元件。为了要讲述这个观念,以下一个例子是「非」Well-formed的文件,因为它包含了两个根元件:
<?xml version="1.0">
<COMMON>Columbine</COMMON>
<BOTANICAL>Aquilegia canadensis</BOTANICAL>
元件的巢状层级可建立父/子元件的关系。每个子元件 (非根元件的元件) 都会放置在其父元件之内,就像我们在第五章所讨论的动态物件模型一样。这个观念我们用下面的例子来展示:
<ROOT>
<PARENT1>
<CHILD1></CHILD1>
<CHILD2></CHILD2>
</PARENT1>
</ROOT>
子元件绝对不能放在其父元件的其它子元件之内。例如,下面这段原始码就是一个非well-formed的XML文件:
<ROOT>
<PARENT1>
<CHILD1>
<CHILD2></CHILD1></CHILD2>
</PARENT1>
</ROOT>
同样地,根元件的前后标签也都不能放在其任一子元件的内部。下面的原始码也不是一个Well-formed的XML文件:
<ROOT>
<PARENT1>
<CHILD1></CHILD1>
<CHILD2></CHILD2>
</ROOT>
</PARENT1>
正如您所看到的,XML文件是非常结构化的文件,而且必须遵循严格但简单的规则,以形成well-formed或valid的文件。这种结构加上特定的层级结构,产生了一个有父子元件关系的家族树状结构。
XML和Internet Explorer
截至目前为止,我们只介绍了XML的资料特性,而未介绍XML的显示方式。接著您一定会有这个逻辑上的疑问,那就是该如何在网页上显示XML资料? Internet Explorer包含了几个可以显示XML资料的物件,最常被用到的就是DOMDocument 物件。这个物件基本上扮演的是资料本身与 Internet Explorer 资料显示功能的中介角色。
当这个物件在分析一份文件时,它会在记忆体中建立文件内所有元件的树状结构;并提供使用XML文件物件模型 (XML Document Object Model) 的方法,也就是允许存取XML资料的介面。这个物件模型会显露物件的性质、方法、事件、及包含在资料树中真正的资料部份。详细的XML 文件物件模型的性质、方法、与事件,在本章稍后的表格21-1到21-3中可找到详细的列表。物件模型可以让设计者检视从根到分支的所有树状结构。
程式列表21-1是一个简单的网页,它将XML资料送进网页中,然后将它显示在萤幕上。图21-1 显示的是网页的浏览结果。
<HTML>
<HEAD>
<TITLE>Code Listing 21-1</TITLE>
<SCRIPT LANGUAGE="JavaScript" FOR="window" EVENT="onload">
start()
</SCRIPT>
<SCRIPT LANGUAGE="JavaScript">
var xmlDoc=new ActiveXObject("Microsoft.XMLDOM")
xmlDoc.async=false
xmlDoc.load("email.xml")
function start()
var root=xmlDoc.documentElement
todata.innerText=root.childNodes.item(0).text
fromdata.innerText=root.childNodes.item(1).text
ccdata.innerText=root.childNodes.item(2).text
subjectdata.innerText=root.childNodes.item(3).text
bodydata.innerText=root.childNodes.item(4).text
</SCRIPT>
</HEAD>
<BODY>
<B>To:</B> <SPAN ID="todata"></SPAN><BR>
<B>From:</B> <SPAN ID="fromdata"></SPAN><BR>
<B>CC:</B> <SPAN ID="ccdata"></SPAN><BR>
<B>Subject:</B> <SPAN ID="subjectdata"></SPAN><BR>
<B>Body:</B> <SPAN ID="bodydata"></SPAN><BR>
</BODY>
</HTML>
程式21-1
图 21-1 一个简单处理XML文件的 HTML网页
在<HEAD>中的第一段Script程式码只是一个很简单的事件处理器,其作用是在浏览器载入网页后,执行start函式的呼叫。我们等一下会解释这个函式。注意网页本身包含了SPAN标签,这些标签所定义的区域将会是最后XML资料的显示空间。
我们先前有提到,XML处理器扮演的是XML文件与HTML网页 (或任何其它类似的显示媒体) 的中间媒介层。我们必须先将XML处理器载入,成为网页上的一个物件,如此我们才能利用Script语言与之沟通。
var xmlDoc=new ActiveXObject("Microsoft.XMLDOM")
xmlDoc.async=false
xmlDoc.load("email.xml")
程式码的第一行载入一个DOMDocument物件,然后将它设定给变数xmlDoc。如此提供使用XML处理器与物件模型的权利。下一行告诉处理器不要「非同步」(Asychronously) 载入文件,这个意思是说网页会等到文件的资料完全下载后再继续显示网页;设定值若改为TRUE,则会使资料与网页的其他部分一起下载,如此一来,就得使用一些侦测 (通常会测试ondataavailable或onreadystatechange事件,或readystate性质),来确定资料在被使用之前已载入。第三行则是让物件载入本章一开始提到的 email.xml 这个 XML 文件。以下是资料树状结构一开始被载入的部份:
DOCUMENT
|---XMLDCL
| |---ATTRIBUTE version "1.0"
+---ELEMENT EMAIL
|---ELEMENT TO
| +---PCDATA "[email protected]"
此树状结构是以文件物件 (Document Object) 作为开端,它包含了一个XML宣告。接著是根元件EMAIL,EMAIL元件包含另一个元件,也就是上面结构图中所显示TO子元件,其值为[email protected],这是一个叫做 PCDATA 的XML资料型态。
一旦网页被下载,start函式就会被呼叫。这个函式其实十分简单,其第一行会建立一个变数称为root,并且设定给XML文件的根节点 (root node),让我们可以存取XML资料树。下一行程式码利用 innerText 性质,将一段文字设定给ID为todata 的SPAN元件,如此可以让文字被显示,而它使用的文字是来自XML根元件的第一个子元件。利用root.childNodes.item(0).text这一段程式码,即可存取根元件的第一个子元件,以取得文字内容。
如果您能了解这段程式,您将会知道如何使用 XMLDocument 控制项来载入一份XML文件,且从XML的结构树中取得资料。这是处理其他XML文件的基础。
虽然上面讲的文件所要花费的功夫,要比简单且可得到相同效果的HTML文件来得多。但是,其实一些使用XML会有的优点我们可以从中了解,最大的优点是网页可以变成一个范本资料。上面所建立的HTML网页可以当作其它可以事先定义模组的XML资料的范本。虽然建立一个HTML文件会需要花一点功夫,但它可重复使用于不同的内容。举例来说,XML文件即使被翻译成法语、德语…等其它语言,仍然可以被插入同一个HTML文件中。
除了利用 DOMDocument 物件外,我们还可以用其它的方法可以将XML资料显示成网页,例如: XML资料来源物件 (Data Source Object)、XSL、和 XSL Pattern。这些技术加入了更多使用XML将资料结构化和储存资料的好处,您可以在微软SBN Workshop网站和随书附赠光碟中找到更多相关资讯。在随书CD中,要看 XML 的资料,请在XML TOC (XML Table of Contents) 中选择任何一个主题即可。若要参考线上版本,可连上MSDN线上资源网站。下面的表格分别列出XML 文件物件模型中的各项性质。
性质 | 说明 |
async | 回传一个布林值,决定是否允许非同步下载。当允许非同步下载时, 传回值为TRUE;若不允许则传回值为FALSE 。读/写。 |
attributes | 传回包含目前节点元件之属性集合物件。若节点不能有属性值,则传 回值为null。唯读。 |
childNodes | 传回包含目前节点元件之所有子节点元件的集合。若目前节点没有任 何子节点,则传回值为null。唯读。 |
doctype | 传回一个包含拥有DTD的文件型态节点。若不含DTD,则传回值为 null。唯读。 |
documentElement | 传回一个包含根元件资料的物件。若没有根元件,则传回值为null。 唯读。 |
firstChild | 传回一个包含目前节点的第一个子节点元件,若目前节点没有任何第 一子节点,则传回值为null。唯读。 |
implementation | 传回DOM为目前XML文件实作所得的物件。唯读。 |
lastChild | 传回一个包含目前节点的最后一个子节点元件,若目前节点没有任何 最后一个子节点,则传回值为null。唯读。 |
nextSibling | 传回一个包含目前文件节点在子节点元件集合中的下一个兄弟元件, 若目前节点没有任何兄弟节点,则传回值为null。唯读。 |
nodeName | 传回一个能代表合格的目前节点(元件,属性,或实体参考)名称字 串,唯读。 |
nodetype | 传回一个数字,它分别代表目前节点的DOM型态 (元件、属性、文 字)。唯读。 |
enodeValu | 传回特定节点的相关纯文字资料,这不是元件的资料值,而是未剖析 相关于节点的纯文字,就好像是属性和处理指令(PI),某种资料型态 会回传null。读/写。 |
ondataavaiable | 设定Ondataavaiable事件处理,只能写。 |
onreadystatechange | 设定Onreadystatechange事件处理,只能写。 |
ownerDocument | 传回一个物件,它包含目前节点的文件之根节点。唯读。 |
parentNode | 传回包含目前节点元件之所有父节点元件的集合。若目前节点没有任 何父节点,则传回值为null。唯读。 |
parseError | 传回一个DOM剖析错误物件,它描述了最后的剖析错误。若没有错 误发生,则传回0。唯读。 |
previousSibling | 传回一个包含目前文件节点在子节点元件集合中的上一个兄弟元件, 若目前节点没有任何兄弟节点,则传回值为null。唯读。 |
readyState | 传回一个数字表示目前的XML文件的状态。唯读。值如下所示:
|
url | 传回最后一个成功下载文件的URL。若文件只存在记忆体中(非从外 部档案载入),则传回null值。唯读。 |
validateOnParse | 提示给剖析器文件是否被确认。若为True,则当被剖析时文件是被 确认的,若为false,文件并无被确认,且被期望为well-formed。 读/写。 |
xml | 传回特定节点和其子孙的XML呈现。唯读。 |
方法 | 说明 |
abort() | 若这个方法在非同步下载期间被呼叫,所有的解析动作将被 停止,在记忆体中文件的任何部分也将被抛弃。 |
appendChild(newChild) | 新增一个节点当作特定节点的最后一个节点。 |
cloneNode(deep) | 建立与一个特定节点完全相同的复制节点,deep参数是一 个布林值,若为True,则不只复制节点而且还复制其子孙元 件。若为False,只有特定节点与其属性被复制。 |
createAttribute(name) | 建立一个特定名称的属性。 |
createCDATASection(data) | 建立一个CDATA区段,其中包含特定字串资料。 |
createComments(data) | 建立一个特定字串的注解。 |
createCommentFragment() | 建立一个新的文件片段,不过并不把它加入文件树中。若要 将它加入树中,必须利用insert方法,例如 insertBefore、 replaceChild、或appendChild。 |
createElement(tagName) | 建立一个特定标签名称(大小写不同)的元件。 |
createEntinyReference | 建立一个特定名称的实体参考,不过并不把它加入文件树(name) 中。若要将它加入树中,必须利用insert方法,例如 insertBefore、replaceChild、或appendChild。 |
createNode(type,name, namespaceURL) | 设定特定型态、名称及名称空间建立一个新的节点。 |
createProcessingInstruction(target,data) | 建立一个包含特定目标和资料的处理指令,不过并不把它加入文件树中。 若要将它加入树中,必须利用insert方法, 例如insertBefore、replaceChild、或 appendChild。 |
createTextnode(data) | 建立一个包含特定资料的新文字节点,不过并不把它加入 文件树中。若要将它加入树中,必须利用insert方法,例如 insertBefore、replaceChild、或 appendChild。 |
getElementsByTagName | 传回一个特定名称的元件集合。使用 "*" 的标签名称设定, (tagname) 则会传回所有在文件能找到的元件。 |
haschildnodes() | 传回True 若节点含有子元件,false则没有。 |
InsertBefore | 在参考节点之前插入一个新子节点,newChild 参数是一个包 (newChild,refChild) 含新子节点位置的物件,而refChild是参考节点的位置。若 不包含refChild参数,新的子节点会被插入在子节点集合的 最后位置。 |
load(url) | 从特定的位置中载入指定的文件,传回True若文件载入成 功,传回False则为否。 |
loadXML(xml/string) | 载入一个XML文件,或从字串来的文件片段,传回True若 文件载入成功,传回False则为否。 nodeformID(idString) 传回一个符合特定值的节点,若有符合的物件则传回此物 件,若此操作失败,传回null值。 |
Parsed | 若目前节点和此节点的所有的子孙节点,已经被剖析及, 则传回值为 True,若节点的任何部分都尚未被剖析,则传 回null。 |
removeChild(child) | 从节点集合中删除特定的子节点,child参数就是包含要被 删除的子节点之元件。 |
replaceChild | 用提供的新节点来取代旧节点。若newChild是null,则旧 (newChild,oldChild) 节点不会被取代,而直接删除。 |
selectNodes(patternString) | 传回一个节点集合元件,此元件包含符合提供 XSL pattern 的节点。 |
selectSingleMode | 传回第一节点符合所提供的XSL pattern,若没有发现任何 (Pattern String) 符合,则传回null。 |
TransformNode(stylesheet) | 传回一个字串包含目前节点的处理结果和它的子节点使用 所提供的XSL样式。 |
事件 | 说明 |
Ondataavailable | 当任何资料可用则会引发此Ondataavailable事件。这个技巧并 不提示在这个文件中有多少资料是可用。 |
Onreadystatechange | 当readystate性质改变,则会引发Onreadystatechange 事件, 但此事件并不会提供ready state的设计值,必须使用readystate 性质来取得目前状态。 |
基础之外的其它Scripting 技术
上面介绍的简单 E-Mail 范例,示范了如何存取XML物件模型,以及使用XML资料。若进一步来观察这个文件和它的script,我们将会看到简单所带来的代价。在我们想尽办法让Script简单一些时,我们加入了一些技巧在其中,以证明script在许多情况下还是不够理想的。请考虑下面的问题:
- 用来存取XML文件树和读出资料的script程式,会假定我们不仅知道有多少元件存在,同时也知道元件的顺序和我们的原始档是相同的,因为我们是利用索引来使用每个元素。这类的 script 程式也许可以用在一些应用程式上,但在其他应用程式上可能就不太适合了。
- script程式能分别为每个元件设定位置。这种做法用在小型文件上可能没什么问题;可是如果用在大型或复杂的文件上,则整个处理程序会变得过于冗长,而且很可能错误百出。
- HTML文件并不完全是资料独立的文件,因为用来配置版面和设定格式的HTML标签会跟文字 (如前一个例中的 To:和 From:) 夹杂在一起。若文件必须要翻译成其他语言,则会需要同时编辑XML文件与HTML两种文件。
我们现在就来看一些script 的写作技巧,以避免我们刚刚所讲的问题。
处理XML文件树
在上一个例子中,设计者必须知道XML文件中元件的数目和类型,但是如果能使用一些XML物件模型中的其它性质和方法,那么事情就会简单一些。
在这些有用的性质当中,其中有一个是childNodes,这个性质会传回目前节点的子节点集合。ChildNodes的length性质会包含子节点的数目。因此,处理节点的子节点将会非常容易。参考如下程式码 21-2。
<HTML>
<HEAD>
<TITLE>Code Listing 21-2</TITLE>
<SCRIPT LANGUAGE="JavaScript">
var xmlDoc=new ActiveXObject("Microsoft.XMLDOM")
xmlDoc.async=false
xmlDoc.load("email.xml")
root = xmlDoc.documentElement
for (var i=0; i<root.childNodes.length; i++){
alert(root.childNodes.item(i).nodeName+" = "+root.childNodes.item(i).text)
}
</SCRIPT>
</HEAD>
</HTML>
程式 21-2
图21-2 对话窗包含第一个元素的名称与内容
现在载入HTML文件后,应该会一连串产生 5 个对话窗,每个对话窗都会包含元件在 XML 文件中的名称与内容。第一个对话窗如图21-2所示。处理的程式利用了root.childNodes.length性质来询问 XML 文件,以得知根元件包含了多少个子元件。接著,script会进入一个回圈,巡行检视根元件的所有子元件,并取得每一个子节点的名称 (利用nodeName性质) 和文字内容,直到最后一个节点为止。
不过,这个程式处理结构树的第一层而已,若根元件的任何子元件也有子元件,则会被程式21-2所忽略。Inbox.xml档案是较为复杂的文件,此文件是一个多层的树结构。注意第一个email甚至包含一个元件(CC),这是在其它email所没有的。
<?xml version="1.0"?>
<INBOX>
<EMAIL>
<TO>[email protected]</TO>
<FROM>[email protected]</FROM>
<CC>[email protected]</CC>
<SUBJECT>My document is a tree.</SUBJECT>
<BODY>This is an example of a tree structure.</BODY>
</EMAIL>
<EMAIL>
<TO>[email protected]</TO>
<FROM>[email protected]</FROM>
<SUBJECT>XML is cool.</SUBJECT>
<BODY>This is a simple message.</BODY>
</EMAIL>
<EMAIL>
<TO>[email protected]</TO>
<FROM>[email protected]</FROM>
<SUBJECT>Here is that code.</SUBJECT>
<BODY>I send too many emails.</BODY>
</EMAIL>
</INBOX>
这需要更复杂的script才能得到和程式21-1相同型态的结果,也就是显示在浏览器中的email内容。程式21-3从XML文件起头开始,然后渐渐地由上往下巡行,并且显示每一个节点的内容。如果有一个节点有子节点,其子节点将会被检视,并且显示其内容。图21-3显示执行此程式的结果。
<HTML>
<HEAD>
<TITLE>Code Listing 21-3</TITLE>
<SCRIPT LANGUAGE="JavaScript">
var xmlDoc = new ActiveXObject("microsoft.xmldom")
xmlDoc.async=false
xmlDoc.load("inbox.xml")
root=xmlDoc.documentElement
newHTML=""
function start(){
buildTree(root)
content.innerHTML=newHTML
}
function buildTree(passedNode){
var children = passedNode.childNodes.length
for (var j=0; j<children; j++){
Node=passedNode.childNodes.item(j)
if (Node.nodeName=="EMAIL"){ newHTML+=("<P>"+Node.nodeName+" "+j) }
if (!Node.hasChildNodes()){
newHTML+=( "<BR><B>"+Node.parentNode.nodeName+":</B> "+Node.text )
}
buildTree(Node)
}
}
</SCRIPT>
</HEAD>
<BODY οnlοad="start()">
<SPAN ID="content"></SPAN>
</BODY>
</HTML>
程式 21-3
图21-3 显示XML文件的内容
我们一起从头到尾来看一下这个程式。当文件被载入后,script区块会载入XML档案,将XML的根元件设定给变数root,接著建立空白newHTML变数,这个变数等一下在script中会用到。当文件结束载入,在BODY标签中的onload事件处理会呼叫start函式,此函式会轮流呼叫buildtree函式,并传送XML根元件给它。
这个例子主要运作是由buildTree函式来完成的,这是一个递回函式,也就是说当一些特殊情况发生时,它会自己呼叫自己。这个函式基本上可以检视目前节点,及检查每个节点多少个子节点。若节点含有子节点,这函式会再次呼叫自己一次,传入第一个子节点给自己。一旦下达一个没有子节点的节点,它会加入newHTML变数及一些HTML原始码,来描述目前节点的资料,然后往上一层回到它的父节点。借由重复这样的动作,它可以巡行检视整个资料树。接著,来深入看一个函式的片段:
var children = passedNode.childNodes.length
for (var j=0; j<children; j++){
函式一开头就找出目前有多少子节点,然后将值设定给children变数。下一行由一个for回圈开始,它会针对目前节点的每一个子节点重复执行。
Node=passedNode.childNodes.item(j)
if (Node.nodeName=="EMAIL"){ newHTML+=("<P>"+Node.nodeName+" "+j) }
if (!Node.hasChildNodes()){
newHTML+=( "<BR><B>"+Node.parentNode.nodeName+":</B>
接著,目前的子节点会被设定给Node变数。由于我们希望资料中的每一个email都可以分开,因此加入了上面的第二行。若节点的名称为 "EMAIL",则除了这个节点的名称和号码之外,还会加入段落标签 <P>到newHTML中,我们也测试是否Node.parentNode==root,因为只有在root之下的元件才有可能是EMAIL元件。
BuildTree(Node)
For回圈的最后一行再次呼叫buildtree函数,并传目前节点过去。最后,每个元件都呼叫了此函式。更弹性且有完整功能的XML tree 巡行程式可以在http://www.microsoft.com/gallery/sample/xml/tree_viewer/default.asp找到,请参阅Tools and Samples和XML Tree
viewer部分。
您应该可以注意到,使用这些技巧可以让HTML文件更加弹性且功能更强,虽然这个文件只是一个特定类型的XML文件范本而已。但是这样的技巧却让HTML与XML能完美的互相补强。
错误处理
XML的物件模型提供了许多方法给设计者来处理 XML 文件所产生的错误。其中一个是文件物件的parseError性质,它提供了XML文件发生问题时的资讯,让设计者可以进行处理。基本上,使用者可能不会需要去处理这样子的错误。
ParseError性质提供了每个可能发生错误的程式码。设计者可以使用这些程式做下列事情:
- 帮忙为XML文件与script除错。
- 告知使用者问题所在,并提供建议的解决方案。
- 将某种错误拦截,然后在背景修复错误。
下面的程式码说明parseError如何告知使用者发生问题。
Var xmlDoc = new ActiveXObject("Microsoft.xmldom");
XmlDoc.load("email.xml");
If (xmlDoc.parseError.reason == " ") {
Alert("Document loaded successfully")
}
else {
alert("The Following error occurred:"+xmlDoc.parseError.errorCode)
}
这个技巧可以用于除错和让使用者可以避开错误。
XML Data Island
在刚刚的例子,我们将XML 当作一个资料来源,并将HTML当作显示的方法。当HTML文件被显示时,XML 与 HTML 会被当作是两个互相影响的独立文件。另一个选择是使用XML data islands。Data islands可以让设计者在HTML文件直接包括XML片段。不过对于这个方法的效果,目前有正反两种意见,正面的意见是:
- 您不需分离出XML文件
- 您不需利用script载入XML资料
- 您不需透过使用Object元素或ActiveXObject物件
另外也有一些负面的意见,端视如何使用data islands:
- XML data 不再独立于HTML档案
- 对于其他应用程式或HTML文件,XML data失去了可携性
- 如果XML的原始码散落在HTML原始码中,XML 也许会更难管理。XML Data Islands可以是行内XML或外部连结XML文件。
处理行内XML Data Islands
要使用行内XML Data Islands,只要很简单地直接插入一段XML程式码到HTML文件就好。XML文件的内容放在<XML></XML>这一组标签中。为了示范,我们修改程式列表 21-1 ,将email.xml嵌入到html文件中,然后将程式码改变成程式列表 21-4。产生的结果和先前的例子完全一样,如图21-1。
<HTML>
<HEAD>
<TITLE>Code Listing 21-4</TITLE>
<XML ID="email">
<EMAIL>
<TO>[email protected]</TO>
<FROM>[email protected]</FROM>
<CC>[email protected]</CC>
<SUBJECT>Basic email</SUBJECT>
<BODY>This is an XML-based email.</BODY>
</EMAIL>
</XML>
<SCRIPT LANGUAGE="JavaScript">
function start(){
xmlDoc=email
var root=xmlDoc.documentElement
todata.innerText=root.childNodes.item(0).text
fromdata.innerText=root.childNodes.item(1).text
ccdata.innerText=root.childNodes.item(2).text
subjectdata.innerText=root.childNodes.item(3).text
bodydata.innerText=root.childNodes.item(4).text
}
</SCRIPT>
</HEAD>
<BODY οnlοad="start()">
<B>To:</B> <SPAN ID="todata"></SPAN><BR>
<B>From:</B> <SPAN ID="fromdata"></SPAN><BR>
<B>CC:</B> <SPAN ID="ccdata"></SPAN><BR>
<B>Subject:</B> <SPAN ID="subjectdata"></SPAN><BR>
<B>Body:</B> <SPAN ID="bodydata"></SPAN><BR>
</BODY>
</HTML>
程式 21-4
首先注意当行内XML Data包含在<XML>标签中,<XML>元素并不是根节点。反之,它是一个HTML标签,让浏览器可以辨别包含在<XML>标签之内就会被当作是XML。也要注意一下<XML>标签有ID属性,所以它可以被script叫用。新的程式码xmlDoc=email相等于使用XMLDOM ActiveXObject,然后会载入一个特定的XML文件。事实上,使用的还是同一个处理程式;只是用不同的方法来存取而已。除了行内XML Data Island,还有一个方法可以用连结外部的XML文件来使用<XML>标签。
处理连结XML Data Islands
要利用<XML>标签来连结XML文件,您只要使用<XML>标签的SRC属性就好。我们可以利用下面的程式码,来取代上述的文件中之整个文件元素。
<XML ID="email" SRC="email.xml"></XML>
此执行结果和前面例子完全相同。这个好处在于XML资料仍然与html文件分离。本质上,这个方法处理的方式十分像ActiveX Control方式,除了浏览器必须负责处理control及载入文件以外。
<XML>标签的属性
<XML>标签包含,某些额外的属性,当文件载入后,发生的事件可提供更多的控制。
VALIDATEONPARSE 属性指出当它被解析时,文件是否应该被确认。它的预设值为True。下面是一个例子:
<XML ID="xmlDoc" SRC="xmldoc.xml" VALIDATEPARSE="false"></XML>
ASYNC 属性意指是否文件要非同步下载。它的预设值为True。下面是一个例子:
<XML ID="xmlDoc" SRC="xmldoc.xml" ASYNC="false"></XML>
XML是否为所有发生在web上问题的解决方案?或者只是个夸大的宣传?事实上都不是。XML已经获得不同领域的业界所支援,而不仅仅是在Web 上而已。这表示会有许多承诺,特别是在不同的浏览器都能支援XML的情况下。本章只是提供十分简单的 XML 概略说明,您可以在W3C网站,及一本不错的XML参考手册( XML in action by William J. Pardi Microsoft Press) 中找到更多XML的资讯。