第19章 E4X
E4X(ECMAScript for XML)为处理 XML 定义了新的语法,也定义了特定于 XML 的对象。
1. E4X 的类型
(1) XML:XML 结构中的任何一个独立的部分。
(2) XMLList:XML 对象的集合。
(3) Namespace:命名空间前缀与命名空间 URI 之间的映射。
(4) QName:由内部名称和命名空间 URI 组成的一个限定名。
(1) XML类型
- XML 的实例可以表现元素、特性、注释、处理指令或文本节点。
- XML 类型继承自 Object 类型,因此它也继承了所有对象默认的所有属性和方法。
创建 XML 对象:
var x = new XML(); // 创建一个空的 XML 对象
//传入到构造函数中的 XML 字符串会被解析为分层的 XML 对象。
var x = new XML("<employee position=\"Software Engineer\"><name>Nicholas " +
"Zakas</name></employee>");
//传入 DOM 文档或节点,以便它们的数据可以通过 E4X 来表现
var x = new XML(xmldom);
//使用 XML 字面量将 XML数据直接指定给一个变量。
var employee = <employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>;
toXMLString()
方法会返回 XML 对象及其子节点的 XML 字符串表示。toString()
方法则会基于不同 XML 对象的内容返回不同的字符串。
var data = <name>Nicholas C. Zakas</name>;
alert(data.toString()); //"Nicholas C. Zakas"
alert(data.toXMLString()); //"<name>Nicholas C. Zakas</name>"
(2) XMLList类型
XMLList 类型表现 XML 对象的有序集合。
创建 XMLList 对象:
//1. 使用 XMLList 构造函数
var list = new XMLList();
//保存在这个 list 变量中的 XMLList 就包含了两个 XML 对象,分别是两个<item/>元素。
var list = new XMLList("<item/><item/>");
//2. 使用加号(+)操作符来组合两个或多个 XML 对象,从而创建 XMLList 对象。
var list = <item/> + <item/> ;
//3. 使用特殊的<>和</>语法来完成
var list = <><item/><item/></>;
//4. 在解析较大的 XML 结构的过程中捎带着
被创建出来的。
var employees = <employees>
<employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>
<employee position="Salesperson">
<name>Jim Smith</name>
</employee>
</employees>;
使用方括号语法及位置来访问每个元素
var firstEmployee = employees.employee[0];
var secondEmployee = employees.employee[1];
每个 XMLList 对象都有length()方法
,用于返回对象中包含的元素对象。
alert(employees.employee.length()); //2
(3) Namespace 空间
创建 Namespace 对象:
var ns = new Namespace();
var ns = new
var wrox = new Namespace("wrox", 命名空间
- 使用
prefix
和uri 属性
来取得 Namespace 对象中的信息。
alert(ns.prefix);//undefined
- 如果 XML 字面量中包含命名空间,或者通过 XML 构造函数解析的 XML 字符串中包含命名空间信息, 那么就会
自动创建 Namespace 对象
。 - 可以通过
前缀
和namespace()方法
来取得对 Namespace 对象的引用。
var xml = <wrox:root
<wrox:message>Hello World!</wrox:message>
</wrox:root>;
var wrox = xml.namespace("wrox");
alert(wrox.uri);
alert(wrox.prefix);
- Namespace 对象的 toString()方法始终会返回命名空间 URI。
(4) QName类型
- 向 QName 构造函数中传 入名称或 Namespace 对象和名称,可以手工创建新的 QName 对象。
var wrox = new Namespace("wrox",
var wroxMessage = new QName(wrox, "message"); //表示"wrox:message"
-
uri 属性
返回在创 建对象时指定的命名空间的 URI(如果未指定命名空间,则返回空字符串),而localName 属性
返回限定名中的内部名称。
alert(wroxMessage.uri);
alert(wroxMessage.localName); //"message"
- QName 对象重写了
toString() 方法
,会以uri::localName形式返回一个字符串,对于前面的例子来说,就是". com/::message"。 - 在解析 XML 结构时,会为表示相应元素或特性的 XML 对象自动创建 QName 对象。可以使用这个 XML 对象的
name()方法
取得与该 XML 对象关联的 QName 对象。
var xml = < wrox:root
<wrox:message>Hello World!</wrox:message>
</wrox:root> ;
var wroxRoot = xml.name();
alert(wroxRoot.uri);
alert(wroxRoot.localName); //"root"
- 使用
setName()方法
并传入一个新 QName 对象,可以修改 XML 对象的限定名。
xml.setName(new QName("newroot"));
- 如果该名称不属于任何命 名空间,则可以像下面这样使用
setLocalName()方法
来修改内部名称:
xml.setLocalName("newtagname");
2. 一般用法
- 可以使用
点号加特性或标签名
的方式来访问其中不同的层次和结构。每个子元素都是父元素的一个属性,而属性名与元素的内部名称相同。
(1) 如果子元素只包含文本,则相应的属性只返回文本。
(2) 如果有多个元素 具有相同的标签名,则会返回 XMLList。
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"
(1) 如果你不确定子元素的内 部名称,或者你想访问所有子元素,不管其名称是什么,也可以像下面这样使用
星号(*)
。
var allChildren = employees.*; //返回所有子元素,不管其名称是什么
alert(employees.*[0].name); //"Nicholas C. Zakas"
(2)
child()方法
将属性名或索引值传递给 child() 方法,也会得到相同的值。
var firstChild = employees.child(0);//与employees.*[0]相同
var employeeList = employees.child("employee");//与 employees.employee 相同
var allChildren = employees.child("*");//与employees.*相同
(3)
children()方法
始终返回所有子元素。
var allChildren = employees.children(); //与employees.*相同
(4)
elements()
的行为与 child()类似,区别仅在于它只返回表示元素的 XML 对象。
var employeeList = employees.elements("employee"); //与 employees.employee 相同
var allChildren = employees.elements("*"); //与employees.*相同
(5) 要删除子元素,可以使用
delete 操作符
。
delete employees.employee[0];
alert(employees.employee.length()); //1
(1) 访问特性
* 访问特性的方法:
- 使用
点语法
,为了区分特性名与子元素的标签名,必须在名称前面加上一个@字符
。
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"
以这种语法访问特性会得到一个
表示特性的 XML 对象
,对象的 toString()方法始终会返回特性的值。要取得特性的名称,可以使用对象的name()方法
。
- 使用
child()方法
来访问特性,只要传入带有@前缀的特性的名称即可。
alert(employees.employee[0].child("@position")); //"Software Engineer"
- 使用
attribute()方法
并传入特性名,可以只访问 XML 对象的特性,不需要传入带@字符的特性名。
alert(employees.employee[0].attribute("position")); //"Software Engineer"
*取得 XML 或 XMLList 对象中的所有特性
可以使用
attributes()方法
,这个方法会返回一个表示所有特性的 XMLList 对象。使用这个方法与使用@*
//下面两种方式都会取得所有特性
var atts1 = employees.employee[0].@*;
var atts2 = employees.employee[0].attributes();
*修改特性的值
employees.employee[0].@position = "Author"; //修改 position 特性
employees.employee[0].@experience = "8 years"; //添加 experience 特性 employees.employee[0].@manager = "Jim Smith"; //添加 manager 特性
*使用 delete 操作符
来删除特性
delete employees.employee[0].@position; //删除 position 特性
(2) 其他节点类型
- 在默认情况上,E4X
不会解析注释或处理指令,
因此这些部分不会出现在最终的对象层次中。如果想让解析器解析这些部分,可以像下面这样设置 XML 构造函数的下列两个属性。在设置了这两个属性之后,E4X 就会将注释和处理指令解析到 XML 结构中。
XML.ignoreComments = false;
XML.ignoreProcessingInstructions = false;
nodeKind()方法
可以得到 XML 对象表示的类型,该访问可能会返回"text"、"element"、"comment"、
"processinginstruction"或"attribute"。
不能在包含多个 XML 对象的 XMLList 上调用 nodeKind()方法;否则,会抛出一个错误。
- 取得特定类型的节点:
(1) attributes():返回 XML 对象的所有特性。
(2) comments():返回 XML 对象的所有子注释节点。
(3) elements(tagName):返回 XML 对象的所有子元素。可以通过提供元素的 tagName(标签名)来过滤想要返回的结果。
(4) processingInstructions(name):返回 XML 对象的所有处理指令。可以通过提供处理指令的 name(名称)来过滤想要返回的结果。
(5) text():返回 XML 对象的所有文本子节点。
- 使用
hasSimpleContent()
和hasComplexContent()方法
,可以确定 XML 对象中是只包含文本, 还是包含更复杂的内容。如果 XML 对象中只包含子文本节点,则前一个方法会返回 true;如果 XML 对 象的子节点中有任何非文本节点,则后一个方法返回 true。
(3) 查询
-
使用两个点
,则可以 进一步扩展查询的深度,查询到所有后代节点。
var allDescendants = employees..*; //取得<employees/>的所有后代节点
-
取得特定标签的元素
,需要将星号替换成实际的标签名。
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";
8.parent()方法
能够在 XML 结构中上溯,这个方法会返回一个 XML 对象,表示当前 XML 对象 的父元素。
var employees2 = employees.employee.parent();//变量 employees2 中包含着与变量 employees 相同的值。
(4) 构建和操作XML
- 可以在XML字面量中嵌入 JavaScript 变量,语法是使用
花括号({})
。
var tagName = "color";
var color = "red";
var xml = <{tagName}>{color}</{tagName}>;
alert(xml.toXMLString()); //"<color>red</color>
- E4X 也支持使用标准的 JavaScript 语法来构建完整的 XML 结构。
var employees = <employees/>;
employees.employee.name = "Nicholas C. Zakas";
employees.employee.@position = "Software Engineer";
//最终构建的 XML 结构
<employees>
<employee position="Software Engineer">
<name >Nicholas C. Zakas</name>
</employee>
</employees>
- 用加号操作符也可以再添加一个<employee/>元素
employees.employee += <employee position="Salesperson">
<name>Jim Smith</name>
</employee>;
//最终构建的 XML 结构
<employees>
<employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>
<employee position="Salesperson">
<name>Jim Smith</name>
</employee>
</employees>
4.类似 DOM 的方法
(1) appendChild(child):将给定的 child 作为子节点添加到 XMLList 的末尾。
(2) copy():返回 XML 对象副本。
(3) insertChildAfter(refNode, child):将 child 作为子节点插入到 XMLList 中 refNode 的后面。
(4) insertChildBefore(refNode, child):将 child 作为子节点插入到 XMLList 中 refNode 的前面。
(5) prependChild(child):将给定的 child 作为子节点添加到 XMLList 的开始位置。
(6) replace(propertyName, value):用 value 值替换名为 propertyName 的属性,这个属性 可能是一个元素,也可能是一个特性。
(7) setChildren(children):用 children 替换当前所有的子元素,children 可以是 XML 对 象,也可是 XMLList 对象。
(5) 解析和序列化
- 与 XML
解析相关
的设置有如下三个。
ignoreComments
:表示解析器应该忽略标记中的注释。默认设置为 true。ignoreProcessingInstructions
:表示解析器应该忽略标记中的处理指令。默认设置为 true。ignoreWhitespace
:表示解析器应该忽略元素间的空格,而不是创建表现这些空格的文本节
点。默认设置为 true。
- 与 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());
(6) 命名空间
- 使用
setNamespace()
并传入 Namespace 对象,也可以为给定元素设置命名空间。
var messages = <messages>
<message>Hello world!</message>
</messages>;
messages.setNamespace(new Namespace("wrox",
<wrox:messages
<message>Hello world!</message>
</wrox:messages>
- 如果只想添加一个命名空间声明,而不想改变元素,可以使用
addNamespace()方法
并传入 Namespace 对象。
messages.addNamespace(new Namespace("wrox",
<messages
<message>Hello world!</message>
</messages>
-
removeNamespace()方法
并传入 Namespace 对象,可以移除表示特定命名空间前缀和 URI的命名空间声明。
messages.removeNamespace(new Namespace("wrox",
- 返回与节点相关的 Namespace 对象的数组
(1)namespaceDeclarations()
返回在给定节点上声明的所有命名空间的数组。
(2)inScopeNamespaces()
返回位于给定节点 作用域中(即包括在节点自身和祖先元素中声明的)所有命名空间的数组。
var messages = <messages
<message>Hello world!</message>
</messages>;
alert(messages.namespaceDeclarations());
alert(messages.inScopeNamespaces());
alert(messages.message.namespaceDeclarations()); //""
alert(messages.message.inScopeNamespaces());
- 使用
双冒号(::)
也可以基于 Namespace 对象来查询 XML 结构中具有特定命名空间的元素。
// 取得包含在 wrox 命名空间中的所有<message/>元素
var messages = <messages
<wrox:message>Hello world!</message>
</messages>;
var wroxNS = new Namespace("wrox",
var wroxMessages = messages.wroxNS::message;
- 以为某个作用域中的所有 XML 对象设置默认命名空间。为此,要使用
default xml namespace
语句,并将一个 Namespace 对象或一个命名空间 URI 作为值赋给它。
default xml namespace =
function doSomething(){
//只为这个函数设置默认的命名空间
default xml namespace = new Namespace("your",
}
3. 其他变化
- 引入了
for-each-in 循环
,以便迭代遍历每一个属性并返回属性的值。
var employees = <employees>
<employee position="Software Engineer">
<name>Nicholas C. Zakas</name>
</employee>
<employee position="Salesperson">
<name>Jim Smith</name>
</employee>
</employees>;
for each (var child in employees){
alert(child.toXMLString());
}
for each (var attribute in employees.@*){ //遍历特性 alert(attribute);
}
- E4X 还添加了一个全局函数,名叫
isXMLName()
。这个函数接受一个字符串,并在这个字符串是 元素或特性的有效内部名称的情况下返回 true。
alert(isXMLName("color")); //true
alert(isXMLName("hello world")); //false
- E4X 对标准 ECMAScript 的最后一个修改是
typeof 操作符
。在对 XML 对象或 XMLList 对象使用 这个操作符时,typeof 返回字符串"xml"。
var xml = new XML();
var list = new XMLList();
var object = {};
alert(typeof xml); //"xml"
alert(typeof list); //"xml"
alert(typeof object); //"object"
4. 全面启用 E4X
要想完整地启用 E4X,需要将<script>标签的 type 特性设置为 2
"text/javascript;e4x=1"