1、E4X的类型
XML:XML结构中的任何一个独立的部分
XMLList:XML对象的集合
Namespace:命名空间前缀和命名空间URL之间的映射
QName:由内部名称和命名空间URL组成的一个限定名
1.1XML类型
var x = new XML("<employee position=\"Software Engineer\"><name>Nicholas " +"Zakas</name></employee>");
传入到构造函数中的 XML 字符串会被解析为分层的 XML 对象。除此之外,还可以向构造函数中传入 DOM 文档或节点,以便它们的数据可以通过 E4X 来表现,语法如下:
var x = new XML(xmldom);
var employee = <employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>;
在这个例子中,我们将一个 XML 数据结构直接指定给了一个变量。这种简洁的语法同样可以创建一个 XML 对象,并将它赋值给 employee 变量。
XML 类型的 toXMLString()方法会返回 XML 对象及其子节点的 XML 字符串表示
var data = <name>Nicholas C. Zakas</name>;
alert(data.toString()); //"Nicholas C. Zakas"
alert(data.toXMLString()); //"<name>Nicholas C. Zakas</name>"
1.2XMLList类型
创建单个XML对象和XML类似,创建多个可使用+号链接、闭合标签,如下:
var list = <item/> + <item/> ;
var list = <><item/><item/></>;
尽管可以创建独立的 XMLList 对象,但是这类对象通常是在解析较大的 XML 结构的过程中捎带着被创建出来的。来看下面的例子:
var employees = <employees>
<employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>
<employee position="Salesperson">
<name>Jim Smith</name>
</employee>
</employees>;
1.3Namespace类型
E4X 中使用 Namespace 对象来表现命名空间。通常, Namespace 对象是用来映射命名空间前缀和命名空间 URI 的,不过有时候并不需要前缀。要创建 Namespace 对象,传入 URI 或前缀加 URI,就可以初始化 Namespace 对象、可以像下面这样使用 Namespace构造函数:
var ns = new Namespace("http://www.wrox.com/"); //没有前缀的命名空间
var wrox = new Namespace("wrox", "http://www.wrox.com/"); //wrox 命名空间
//可以使用 prefix 和 uri 属性来取得 Namespace 对象中的信息
如果 XML 字面量中包含命名空间, 或者通过 XML 构造函数解析的 XML 字符串中包含命名空间信息,那么就会自动创建 Namespace 对象。然后,就可以通过前缀和 namespace()方法来取得对 Namespace对象的引用。来看下面的例子:
var xml = <wrox:root xmlns:wrox="http://www.wrox.com/">
<wrox:message>Hello World!</wrox:message>
</wrox:root>;
var wrox = xml.namespace("wrox");
alert(wrox.uri);
alert(wrox.prefix);
1.4、QName类型
QName 类型表现的是 XML 对象的限定名,即命名空间与内部名称的组合。向 QName 构造函数中传入名称或 Namespace 对象和名称,可以手工创建新的 QName 对象,如下所示:
var wrox = new Namespace("wrox", "http://www.wrox.com/");
var wroxMessage = new QName(wrox, "message"); //表示"wrox:message"
创建了 QName 对象之后,可以访问它的两个属性: uri 和 localName。其中, uri 属性返回在创建对象时指定的命名空间的 URI(如果未指定命名空间,则返回空字符串),而 localName 属性返回限定名中的内部名称,如下面的例子所示:
alert(wroxMessage.uri); //"http://www.wrox.com/"
alert(wroxMessage.localName); //"message"
//这两个属性只读
在解析 XML 结构时,会为表示相应元素或特性的 XML 对象自动创建 QName 对象。可以使用这个 XML对象的 name()方法取得与该 XML 对象关联的 QName 对象,如下面的例子所示:
var xml = < wrox:root xmlns:wrox="http://www.wrox.com/">
<wrox:message>Hello World!</wrox:message>
</wrox:root> ;
var wroxRoot = xml.name();
alert(wroxRoot.uri); //"http://www.wrox.com/"
alert(wroxRoot.localName); //"root"
//使用 setName()方法并传入一个新 QName 对象,可以修改 XML 对象的限定名,如下所示:
xml.setName(new QName("newroot"));
//通常,这个方法会在修改相应命名空间下的元素标签名或特性名时用到。如果该名称不属于任何命名空间,则可以像下面这样使用 setLocalName()方法来修改内部名称:
xml.setLocalName("newtagname");
2、一般用法
在将 XML 对象、元素、特性和文本集合到一个层次化对象之后,就可以使用点号加特性或标签名的方式来访问其中不同的层次和结构。每个子元素都是父元素的一个属性,而属性名与元素的内部名称相同。如果子元素只包含文本,则相应的属性只返回文本,如下面的例子所示:
// 单个元素
var employee = <employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>;
alert(employee.name); //"Nicholas C. Zakas"
// 多个元素
var employees = <employees>
<employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>
<employee position="Salesperson">
<name>Jim Smith</name>
</employee>
</employees>;
alert(employees.employee[0].name); //"Nicholas C. Zakas"
alert(employees.employee[1].name); //"Jim Smith"
// 多个元素不确定子元素的名称
var allChildren = employees.*; //返回所有子元素,不管其名称是什么
alert(employees.*[0].name); //"Nicholas C. Zakas"
// 要获取子元素,除了属性,还能如下
var firstChild = employees.child(0); //与 employees.*[0]相同
var employeeList = employees.child("employee"); //与 employees.employee 相同
var allChildren = employees.child("*"); //与 employees.*相同
// 为了再方便一些,还有一个 children()方法始终返回所有子元素。例如:
var allChildren = employees.children(); //与 employees.*相同
// 而另一个方法 elements()的行为与 child()类似,区别仅在于它只返回表示元素的 XML 对象。例如:
var employeeList = employees.elements("employee"); //与 employees.employee 相同
var allChildren = employees.elements("*"); //与 employees.*相同
//这些方法为 JavaScript 开发人员提供了访问 XML 数据的较为熟悉的语法。要删除子元素,可以使用 delete 操作符,如下所示:
delete employees.employee[0];
alert(employees.employee.length()); //1
2.1、访问特性
访问特性也可以使用点语法,不过其语法稍有扩充。为了区分特性名与子元素的标签名,必须在名称前面加上一个@字符。这是从 XPath 中借鉴的语法; XPath 也是使用@来区分特性和标签的名称。不过,结果可能就是这种语法看起来比较奇怪,例如
var employees = <employees>
<employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>
<employee position="Salesperson">
<name>Jim Smith</name>
</employee>
</employees>;
alert(employees.employee[0].@position); //"Software Engineer"
//使用 attribute()方法并传入特性名,可以只访问 XML 对象的特性。与 child()方法不同,使用 attribute()方法时,不需要传入带@字符的特性名。下面是一个例子。
alert(employees.employee[0].attribute("position")); //"Software Engineer"
//要取得 XML 或 XMLList 对象中的所有特性,可以使用 attributes()方法。这个方法会返回一个表示所有特性的 XMLList 对象。使用这个方法与使用@*的结果相同,如下面的例子所示。
//下面两种方式都会取得所有特性
var atts1 = employees.employee[0].@*;
var atts2 = employees.employee[0].attributes();
//在 E4X 中修改特性的值与修改属性的值一样非常简单,只要像下面这样为特性指定一个新值即可。
employees.employee[0].@position = "Author"; //修改 position 特性
//修改的特性会在内部反映出来,换句话说,此后再序列化 XML 对象,就会使用新的特性值。同样,为特性赋值的语法也可以用来添加新特性,如下面的例子所示。
employees.employee[0].@experience = "8 years"; //添加 experience 特性
employees.employee[0].@manager = "Jim Smith"; //添加 manager 特性
//由于特性与其他 ECMAScript 属性类似,因此也可以使用 delete 操作符来删除特性,如下所示。
delete employees.employee[0].@position; //删除 position 特性
2.2其他节点类型
E4X 定义了表现 XML 文档中所有部分的类型,包括注释和处理指令。在默认情况上, E4X 不会解析注释或处理指令,因此这些部分不会出现在最终的对象层次中。如果想让解析器解析这些部分,可以像下面这样设置 XML 构造函数的下列两个属性。
XML.ignoreComments = false;
XML.ignoreProcessingInstructions = false;
// 在设置了这两个属性之后, E4X 就会将注释和处理指令解析到 XML 结构中。
由于 XML 类型可以表示所有节点,因此必须有一种方式来确定节点类型。使用 nodeKind()方法可以得到 XML 对象表示的类型,该访问可能会返回"text"、 "element"、 "comment"、 "processinginstruction"或"attribute"。以下面的 XML 对象为例。
var employees = <employees>
<?Dont forget the donuts?>
<employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>
<!--just added-->
<employee position="Salesperson">
<name>Jim Smith</name>
</employee>
</employees> ;
可以通过下面的表格来说明 nodeKind()返回的节点类型。
2.3查询
//前面我们看到的大多数例子都使用点语法来访问直接的子节点。而像下面这样使用两个点,则可以进一步扩展查询的深度,查询到所有后代节点。
var allDescendants = employees..*; //取得<employees/>的所有后代节点
//上面的代码会返回<employees/>元素的所有后代节点。结果中将会包含元素、文本、注释和处理指令,最后两种节点的有无取决于在 XML 构造函数上的设置(前面曾经讨论过);但结果中不会包含特性。要想取得特定标签的元素,需要将星号替换成实际的标签名。
var allNames = employees..name; //取得作为<employees/>后代的所有<name/>节点
//同样的查询可以使用 descendants()方法来完成。在不给这个方法传递参数的情况下,它会返回所有后代节点(与使用..*相同),而传递一个名称作为参数则可以限制结果。下面就是这两种情况的例子。
var allDescendants = employees.descendants(); //所有后代节点
var allNames = employees.descendants("name"); //后代中的所有<name/>元素
//还可以取得所有后代元素中的所有特性,方法是使用下列任何一行代码。
var allAttributes = employees..@*; //取得所有后代元素中的所有特性
var allAttributes2 = employees.descendants("@*"); //同上
//与限制结果中的后代元素一样,也可以通过用完整的特性名来替换星号达到过滤特性的目的。 例如:
var allAttributes = employees..@position; //取得所有 position 特性
var allAttributes2 = employees.descendants("@position"); //同上
//除了访问后代元素之外,还可以指定查询的条件。例如,要想返回 position 特性值为"Salesperson"的所有<employee/>元素,可以使用下面的查询:
var salespeople = employees.employee.(@position == "Salesperson");
//同样的语法也可以用于修改 XML 结构中的某一部分。例如,可以将第一位销售员( salesperson)的 position 特性修改为"Senior Salesperson",代码如下:
employees.employee.(@position == "Salesperson")[0].@position= "Senior Salesperson";
//注意,圆括号中的表达式会返回一个包含结果的 XMLList,而方括号返回其中的第一项,然后我们
重写了@position 属性的值。
//使用 parent()方法能够在 XML 结构中上溯,这个方法会返回一个 XML 对象,表示当前 XML 对象的父元素。如果在 XMLList 上调用 parent()方法,则会返回列表中所有对象的公共父元素。下面是一个例子。
var employees2 = employees.employee.parent();
//这里,变量 employees2 中包含着与变量 employees 相同的值。在处理来源未知的 XML 对象时,经常会用到 parent()方法。
2.4构建和操作XML
将 XML 数据转换成 XML 对象的方式有很多种。前面曾经讨论过,可以将 XML 字符串传递到 XML构造函数中,也可以使用 XML 字面量。相对而言, XML 字面量方式更方便一些,因为可以在字面量中嵌入 JavaScript 变量,语法是使用花括号( {})。可以将 JavaScript 变量嵌入到字面量中的任意位置上,如下面的例子所示。
var tagName = "color";
var color = "red";
var xml = <{tagName}>{color}</{tagName}>;
alert(xml.toXMLString()); //"<color>red</color>
除了上面介绍的基本的 XML 构建语法之外,还有一些类似 DOM 的方法,简介如下。
appendChild(child):将给定的 child 作为子节点添加到 XMLList 的末尾。
copy():返回 XML 对象副本。
insertChildAfter(refNode, child):将 child 作为子节点插入到 XMLList 中 refNode 的后面。
insertChildBefore(refNode, child):将 child 作为子节点插入到 XMLList 中 refNode 的前面。
prependChild(child):将给定的 child 作为子节点添加到 XMLList 的开始位置。
replace(propertyName, value):用 value 值替换名为 propertyName 的属性,这个属性可能是一个元素,也可能是一个特性。
setChildren(children):用 children 替换当前所有的子元素, children 可以是 XML 对象,也可是 XMLList 对象。
这些方法既非常有用,也非常容易使用。下列代码展示了这些方法的用途。
2.5解析和序列化
E4X 将解析和序列化数据的控制放在了 XML 构造函数的一些设置当中。与 XML 解析相关的设置有如下三个。
ignoreComments:表示解析器应该忽略标记中的注释。默认设置为 true。
ignoreProcessingInstructions: 表示解析器应该忽略标记中的处理指令。默认设置为 true。
ignoreWhitespace:表示解析器应该忽略元素间的空格,而不是创建表现这些空格的文本节点。默认设置为 true。
这三个设置会影响对传入到 XML 构造函数中的字符串以及 XML 字面量的解析。
另外,与 XML 数据序列化相关的设置有如下两个。
prettyIndent:表示在序列化 XML 时,每次缩进的空格数量。默认值为 2。
prettyPrinting:表示应该以方便人类认读的方式输出 XML,即每个元素重起一行,而且子元素都要缩进。默认设置为 true。
这两个设置将影响到 toString()和 toXMLString()的输出。以上五个设置都保存在 settings 对象中,通过 XML 构造函数的 settings()方法可以取得这个对象,如下所示。
var settings = XML.settings();
alert(settings.ignoreWhitespace); //true
alert(settings.ignoreComments); //true
//通过向 setSettings()方法中传入包含全部 5 项设置的对象,可以一次性指定所有设置。在需要临时改变设置的情况下,这种设置方式非常有用,如下所示。
var settings = XML.settings();
XML.prettyIndent = 8;
XML.ignoreComments = false;
//执行某些处理
XML.setSettings(settings); //重置前面的设置
//而使用 defaultSettings()方法则可以取得一个包含默认设置的对象,因此任何时候都可以使用下面的代码重置设置。
XML.setSettings(XML.defaultSettings());
3、其他变化
for-each-in 循环,以便迭代遍历每一个属性并返回属性的值,
全局函数,名叫 isXMLName()。这个函数接受一个字符串,并在这个字符串是元素或特性的有效内部名称的情况下返回 true。在使用未知字符串构建 XML 数据结构时,这个函数可以为开发人员提供方便。
4、全面启用E4X
鉴于 E4X 在很多方面给标准 JavaScript 带来了不同,因此 Firefox 在默认情况下只启用 E4X 中与其他代码能够相安无事的那些特性。要想完整地启用 E4X,需要将<script>标签的 type 特性设置为"text/javascript;e4x=1",例如:
<script type="text/javascript;e4x=1" src="e4x_file.js"></script>
在打开这个“开关”之后,就会全面启用 E4X,从而能够正确地解析嵌入在 E4X 字面量中的注释和 CData 片段。在没有完整启用 E4X 的情况下使用注释和/或 CData 片段会导致语法错误。