当前位置: 首页 > 工具软件 > E4X > 使用案例 >

42-JavaScript高级程序设计-E4X

詹夕
2023-12-01

定义

E4X(ECMAScript for XML):E4X 本身不是一门语言,它只是 ECMAScript 语言的可选扩展。
就其本身而言,E4X 为处理 XML 定义了新的语法,也定义了特定于 XML 的对象。

一、E4X 的类型

E4X 定义了几个新的全局类型:
XML:XML 结构中的任何一个独立的部分。
XMLList:XML 对象的集合。
Namespace:命名空间前缀与命名空间 URI 之间的映射。
QName:由内部名称和命名空间 URI 组成的一个限定名。
这 4 个类型可以表现 XML 文档中的所有部分,内部机制是将每一种类型(特别是XML 和 XMLList)都映射为多个 DOM 类型。

1. XML类型

XML 类型可以用来表现 XML 结构中任何独立的部分。
XML 的实例可以表现元素、特性、注释、处理指令或文本节点。
XML 类型继承自 Object 类型,继承了所有对象默认的所有属性和方法。

创建 XML 对象的方式:

  1. 构造函数
// 创建一个空的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);
  1. XML 字面量(XML 字面量就是嵌入到 JavaScript 代码中的 XML 代码)
// 可以使用XML字面量将XML数据直接指定给一个变量
var employee = <employee position="Software Engineer">
                    <name>Nicholas C. Zakas</name>
               </employee>;

XML 类型的字符串方法:
toXMLString():返回 XML 对象及其子节点的 XML 字符串表示。
toString():基于不同 XML 对象的内容返回不同的字符串。如果内容简单(纯文本)则返回文本;否则与 toXMLString() 方法返回的字符串一样。

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();
var list = new XMLList("<item/><item/>");
  1. 组合两个或多个 XML 对象时创建
// 加号(+)操作符
var list = <item/> + <item/> ;
// 特殊的<>和</>语法
var list = <><item/><item/></>;
  1. 解析较大的 XML 结构的过程中捎带着被创建
var employees = <employees>
			        <employee position="Software Engineer">
			            <name>Nicholas C. Zakas</name>
			        </employee>
			        <employee position="Salesperson">
			            <name>Jim Smith</name>
			        </employee>
			    </employees>;

方法:
length() 方法:用于返回对象中包含的元素数量(是方法不是属性!)
toString()toXMLString() 方法:返回相同的字符串值,也就是将其包含的 XML 对象序列化之后再拼接起来的结果。

注意:
E4X 有意模糊 XML 和 XMLList 类型之间的区别。
实际上,一个 XML 对象与一个只包含一个 XML 对象的 XMLList 之间,并没有显而易见的区别。为了减少两者之间的区别,每个 XML 对象也同样有一个 length() 方法和一个由[0]引用的属性(返回 XML 对象自身)。

3. Namespace类型

E4X 中使用 Namespace 对象来表现命名空间。
通常,Namespace 对象是用来映射命名空间前缀和命名空间 URI 的,不过有时候并不需要前缀。

创建 Namespace 对象:

  1. 使用 Namespace 构造函数
var ns = new Namespace();
var ns = new Namespace("http://www.wrox.com/"); //没有前缀的命名空间
var wrox = new Namespace("wrox", "http://www.wrox.com/"); //wrox命名空间

要想创建默认的命名空间,应该将前缀设置为空字符串。

  1. XML 字面量自动创建
    如果 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); //"http://www.wrox.com/"
alert(wrox.prefix); //"wrox"

属性:
可以使用 prefixuri 属性来取得 Namespace 对象中的信息。
方法:
Namespace 对象的 toString() 方法始终会返回命名空间 URI。

4. QName类型

QName 类型表现的是 XML 对象的限定名,即命名空间与内部名称的组合。

创建 QName 对象:

  1. QName 构造函数
var wrox = new Namespace("wrox", "http://www.wrox.com/");
var wroxMessage = new QName(wrox, "message"); //表示"wrox:message"
  1. 解析 XML 结构时自动创建
    在解析 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"

属性:(这两个属性是只读的)
uri 属性:返回在创建对象时指定的命名空间的 URI (如果未指定命名空间,则返回空字符串)
localName 属性:返回限定名中的内部名称

方法:
QName 对象重写了 toString() 方法,会以uri::localName形式返回一个字符串。

setName():使用 setName() 方法并传入一个新 QName 对象,可以修改 XML 对象的限定名。
通常用于修改相应命名空间下的元素标签名或特性名。
xml.setName(new QName("newroot"));
setLocalName():如果该名称不属于任何命名空间,可以使用 setLocalName() 方法来修改内部名称。
xml.setLocalName("newtagname");

二、一般用法

1. 基本用法

在将 XML 对象、元素、特性和文本集合到一个层次化对象之后,就可以使用点号加特性或标签名的方式来访问其中不同的层次和结构。
每个子元素都是父元素的一个属性,属性名与元素的内部名称相同。如果子元素只包含文本,相应属性只返回文本。
如果有多个元素具有相同的标签名,返回 XMLList。

var employee = <employee position="Software Engineer">
                   <name>Nicholas C. Zakas</name>
               </employee>;
alert(employee.name); //"Nicholas C. Zakas"

child():将属性名或索引值传递给 child() 方法,也会得到与使用属性相同的值。

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.*相同

delete 操作符:删除子元素。

delete employees.employee[0];
alert(employees.employee.length()); //1
2. 访问特性

方式1:可以使用点语法访问特性,为了区分特性名与子元素的标签名,必须在名称前面加上一个@字符。(XPath 也是使用@来区分特性和标签的名称)

alert(employees.employee[0].@position); //"Software Engineer"

方式2:可以使用 child() 方法来访问特性,只要传入带有@前缀的特性的名称。

alert(employees.employee[0].child("@position")); //"Software Engineer"

方式3:使用 attribute() 方法并传入特性名,可以只访问 XML 对象的特性。与 child()方法不同,使用 attribute()方法时,不需要传入带@字符的特性名。

alert(employees.employee[0].attribute("position")); //"Software Engineer"

这三种访问特性的方式同时适用于 XML 和 XMLList 类型。对于 XML 对象来说,会返回一个表示相应特性的 XML 对象;对 XMLList 对象来说,会返回一个 XMLList 对象,其中包含列表中所有元素的特性 XML 对象。

访问所有特性:
要取得 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 特性

删除特性:delete 操作符

delete employees.employee[0].@position; //删除 position 特性
3. 其他节点类型

E4X 定义了表现 XML 文档中所有部分的类型,包括注释和处理指令。
在默认情况上,E4X 不会解析注释或处理指令,因此这些部分不会出现在最终的对象层次中。
设置 E4X 将注释和处理指令解析到 XML 结构中:
XML.ignoreComments = false;
XML.ignoreProcessingInstructions = false;

nodeKind():获取 XML 对象表示的类型
可能会返回"text"、“element”、“comment”、“processing- instruction"或"attribute”。
不能在包含多个 XML 对象的 XMLList 上调用 nodeKind()方法。

只取得特定类型的节点:
attributes():返回 XML 对象的所有特性。
comments():返回 XML 对象的所有子注释节点。
elements(tagName):返回 XML 对象的所有子元素。可以通过提供元素的 tagName(标签名)来过滤想要返回的结果。
processingInstructions(name):返回 XML 对象的所有处理指令。可以通过提供处理指令的 name(名称)来过滤想要返回的结果。
text():返回 XML 对象的所有文本子节点。
上述每一个方法都返回一个包含适当 XML 对象的 XMLList。

hasSimpleContent()hasComplexContent():确定 XML 对象中是只包含文本,还是包含更复杂的内容。
如果 XML 对象中只包含子文本节点,前一个方法会返回 true;如果 XML 对象的子节点中有任何非文本节点,后一个方法返回 true。

4. 查询

(1) 访问后代元素
点语法可以访问直接的子节点。
使用两个点,可以进一步扩展查询的深度,查询到所有后代节点。

var allDescendants = 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"); //同上

(2) 指定查询条件

// 返回position特性值为"Salesperson"的所有<employee/>元素
var salespeople = employees.employee.(@position == "Salesperson");

修改 XML 结构中的某一部分:

// 将第一位销售员(salesperson)的position特性修改为"Senior Salesperson"
employees.employee.(@position == "Salesperson")[0].@position= "Senior Salesperson";

(3) 查找父元素
parent():能够在 XML 结构中上溯,返回一个 XML 对象,表示当前 XML 对象的父元素。
如果在 XMLList 上调用 parent()方法,则会返回列表中所有对象的公共父元素。

var employees2 = employees.employee.parent();
5. 构建和操作XML

1.可以在字面量中嵌入 JavaScript 变量,语法是使用花括号({})

var tagName = "color";
var color = "red";
var xml = <{tagName}>{color}</{tagName}>;
alert(xml.toXMLString()); //"<color>red</color>

2.标准的 JavaScript 语法来构建

var employees = <employees/>;
employees.employee.name = "Nicholas C. Zakas";
employees.employee.@position = "Software Engineer";

加号操作符可以再添加一个<employee/>元素

employees.employee += <employee position="Salesperson">
                          <name>Jim Smith</name>
                      </employee>;

构建结果:

<employees>
    <employee position="Software Engineer">
        <name>Nicholas C. Zakas</name>
    </employee>
    <employee position="Salesperson">
        <name>Jim Smith</name>
    </employee>
</employees>

3.一些类似 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 对象。

6. 解析和序列化

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());
7. 命名空间

namspace():可以取得与特定前缀对应的 Namespace 对象。
setNamespace():传入 Namespace 对象,可以为给定元素设置命名空间。

var messages = <messages>
			       <message>Hello world!</message>
			   </messages>;
messages.setNamespace(new Namespace("wrox", "http://www.wrox.com/"));
// 结果为:
<wrox:messages xmlns:wrox="http://www.wrox.com/">
    <message>Hello world!</message>
</wrox:messages>

addNamespace():传入 Namespace 对象,只添加一个命名空间声明,而不改变元素。

messages.addNamespace(new Namespace("wrox", "http://www.wrox.com/"));
// 应用于前面messages,结果为:
<messages xmlns:wrox="http://www.wrox.com/">
    <message>Hello world!</message>
</messages>

removeNamespace():传入 Namespace 对象,可以移除表示特定命名空间前缀和 URI 的命名空间声明。
注意,必须传入丝毫不差的表示命名空间的 Namespace 对象。

// 可以移除 wrox 命名空间。不过,引用前缀的限定名不会受影响
messages.removeNamespace(new Namespace("wrox", "http://www.wrox.com/"));

namespaceDeclarations()inScopeNamespaces():可以返回与节点相关的 Namespace 对象的数组。
前者返回在给定节点上声明的所有命名空间的数组,后者返回位于给定节点作用域中(即包括在节点自身和祖先元素中声明的)所有命名空间的数组。

var messages = <messages xmlns:wrox="http://www.wrox.com/">
		           <message>Hello world!</message>
		       </messages>; 
alert(messages.namespaceDeclarations()); //"http://www.wrox.com"
alert(messages.inScopeNamespaces()); //",http://www.wrox.com"
alert(messages.message.namespaceDeclarations()); //"" 
alert(messages.message.inScopeNamespaces()); //",http://www.wrox.com"

<messages/>元素在调用 namespaceDeclarations()时,会返回包含一个命名空间的数组,在调用 inScopeNamespaces()时,会返回包含两个命名空间的数组。作用域中的这两个命名空间,分别是默认命名空间(由空字符串表示)和 wrox 命名空间。

双冒号(:: ):可以基于 Namespace 对象来查询 XML 结构中具有特定命名空间的元素。

// 取得包含在 wrox 命名空间中的所有<message/>元素
var messages = <messages xmlns:wrox="http://www.wrox.com/">
				   <wrox:message>Hello world!</message>
			   </messages>;
var wroxNS = new Namespace("wrox", "http://www.wrox.com/");
var wroxMessages = messages.wroxNS::message;

为某个作用域中的所有 XML 对象设置默认命名空间:使用 default xml namespace 语句,并将一个 Namespace 对象或一个命名空间 URI 作为值赋给它。

default xml namespace = "http://www.wrox.com/";
// 在doSomething()函数体内设置默认命名空间并不会改变全局作用域中的默认XML命名空间
function doSomething(){
  //只为这个函数设置默认的命名空间
  default xml namespace = new Namespace("your", "http://www.yourdomain.com");
}

三、其他变化

  1. for-each-in 循环:迭代遍历每一个属性并返回属性的值
for each (var child in employees){
    alert(child.toXMLString());
}
for each (var attribute in employees.@*){ //遍历特性 
    alert(attribute);
}
  1. E4X 添加了一个全局函数 isXMLName()
    这个函数接受一个字符串,并在这个字符串是元素或特性的有效内部名称的情况下返回 true。
alert(isXMLName("color"));           //true
alert(isXMLName("hello world"));     //false
  1. typeof 操作符
    在对 XML 对象或 XMLList 对象使用这个操作符时,typeof 返回字符串"xml"。在对其他对象使用这个操作符时,返回的都是"object"。
var xml = new XML();
var list = new XMLList();
var object = {};
alert(typeof xml); //"xml" 
alert(typeof list); //"xml" 
alert(typeof object); //"object"

四、全面启用E4X

Firefox 在默认情况下只启用 E4X 中与其他代码能够相安无事的那些特性。
要想完整地启用 E4X,需要将<script>标签的 type 特性设置为"text/javascript;e4x=1"。

<script type="text/javascript;e4x=1" src="e4x_file.js"></script>

小结

E4X 是以 ECMA-357 标准的形式发布的对 ECMAScript 的一个扩展。E4X 的目的是为操作 XML 数据提供与标准 ECMAScript 更相近的语法。
到 2011 年底,Firefox 还是唯一一个支持 E4X 的浏览器。

E4X已被遗弃!!!在Firefox 17默认网页中已被禁用,Firefox 20浏览器默认禁用,并已在Firefox 21删除。官方推荐使用DOMParser / XMLSerializer或非jxon算法代替。

本章节主要按红宝书记录了一下笔记。实际上已经没有什么应用。


上一篇:41-JavaScript高级程序设计-JavaScript与XML
下一篇:43-JavaScript高级程序设计-JSON

全书整理版:《Javascript高级程序设计》第3版(总结版)


 类似资料: