javascript与XML
曾几何时,XML一度成为存储和通过因特网传输结构化数据的标准,之前,浏览器无法解析XML数据时,开发人员都要手动编写自己的XML解析器。而自从DOM出现后,所有浏览器都内置了对XML的原生支持(XML DOM),同时也提供了一些相关的技术支持。
创建空白的XML文档:
DOM2级在document.implementation中引入了createDocument()方法。IE9+、Firefox、Opera、Chrome、safari都支持者个方法:
var xmldom=document.implementation.createDocument(namespaceUri,root,doctype);
在通过javascript处理XML时,通常只使用参数root,因为这个参数指定的是XML DOM文档元素的标签名。而namespaceUri参数则很少用到,原因是在javascript中管理命名空间比较困难。最后,doctype参数用的就更少了。
因此先要创建一个新的、文档元素为<root>的XML文档,可以使用如下代码:
var xmldom=document.implementation.createDocument("","root",null);
console.log(xmldom.documentElement.tagName);//"root"
var child=xmldom.createElement("child");
xmldom.documentElement.appendChild(child);//创建一个子元素并加入到心创建的XML文档内
以上是创建了一个无默认命名空间,没指定文档类型的,标签名为root的XML DOM文档。并创建了一个子元素,插入了进去。
如果需要检测浏览器是否支持DOM2级XML,可以使用如下代码:
var hasXmlDom=document.implementation.hasFeature("XML","2.0");
console.log(hasXmlDom);//true
DOMParser类型:(将XML转化为DOM文档)
为了将XML解析为DOM文档,firefox引入了DOMParser类型,后来IE9、safari、chrome、opera也支持了这个类型。在解析XML之前,首先必须创建一个DOMParser的实例,然后再调用parseFromString()方法,这个方法接收两个参数:要解析的XML字符串和内容类型(内容类型始终应该是“text/xml”)。返回的值是一个document的实例。例如:
var parser =new DOMParser();//创建一个DOMParser的实例 var xmldom=parser.parseFromString("<root><child></child></root>","text/xml"); console.log(xmldom.documentElement.tagName);//"root" console.log(xmldom.documentElement.firstChild.tagName);//"child" var anotherChild=xmldom.createElement("child"); xmldom.documentElement.appendChild(anotherChild); var children=xmldom.getElementsByTagName("child"); console.log(children.length);//2
上述代码我们把一个简单的XML字符串解析成了一个DOM文档。以<root>作为其文档元素,该元素还有一个<child>子元素。此后就可以使用DOM方法对返回的这个文档进行操作了。
DOMParser只能解析格式良好的XML,因此不能把HTML解析为HTML文档,在发生解析错误时,仍然会从parseFromString()中返回一个Document对象,但这个对象的文档元素是<parsererror>,而文档元素的内容是对解析错误的描述。
比如讲上面的xml字符串中的root标签少写一个结束标签,就会返回一个文档元素是<parsererror>的document对象。
因为每个浏览器再解析XML发生错误时,反应不同,但都会返回一个<parsererror>元素,所以可以用一个try-catch语句块来检测是否解决错误:
var parser=new DOMParser(), xmldom, errors; try{ xmldom=parser.parseFromString("<root>","text/xml");//少一个root的结束标签,肯定会有错误 errors=xmldom.getElementsByTagName("parsererror"); if(errors.length>0){//应对firefox和opera throw new Error("parsing error!"); } }catch(ex){ console.log("parseing error!"); }
XMLSerializer类型:(将DOM转化为XML)
在引入DOMParser的同时,firefox还引入XMLSerializer类型,提供了相反的功能:将DOM文档序列化为XML字符串。后来IE9+、opera、chrome、safari都支持了XMLSerializer。
要序列化DOM文档,首先必须创建XMLSerialize的实例,然后将文档传入其XMLSerializeToString()方法,例:
var parser=new DOMParser(); var xmldom=parser.parseFromString("<root><child></child></root>","text/xml"); var serializer=new XMLSerializer(); var xml=serializer.serializeToString(xmldom); console.log(xml);
但是,serializerToString()方法返回的字符串并不适合打印,因此看起来乱糟糟的。
XMLSerializer可以序列化任何有效的DOM对象,不仅包括个别的节点,也包括HTML文档,将HTML文档传入serializeToString()以后,HTML文档将被视为XML文档,因此得到的代码也将是良好的。
如果将非DOM对象传入serializeToString(),会导致错误。
IE8之前的版本中的XML:
IE是第一个原生支持XML的浏览器,而这一支持是通过ActiveX对象实现的。为了便于左面应用程序开发人员处理XML,微软创建了MSXML库;但微软并没有针对javascript创建不同的对象,而只是让web开发人员能够通过浏览器访问相同的对象。
通过IE中的ActiveXObject类型可以在javascript中创建ActiveX对象的实例。同样,要创建一个XML文档的实例,也要使用ActiveXObject构造函数并为其传入一个表示XML文档版本的字符串。有6种不同的XML文档版本可以供选择。
Microsoft.XmlDom: | 最初随同IE发布;不建议使用 |
MSXML2.DOMDocument: | 为方便脚本处理而更新的版本,建议仅在特殊情况下作为后备版本使用。 |
MSXML2.DOMDocument.3.0: | 为了再javascript中使用,这是最低的建议版本 |
MSXML2.DOMDocument.4.0: | 在通过脚本处理时并不可靠,使用这个版本可能导致安全警告。 |
MSXML2.DOMDocument.5.0: | 在通过脚本处理时并不可靠,使用这个版本可能导致安全警告。 |
MSXML2.DOMDocument.6.0: | 通过脚本能够可靠处理的最新版本。 |
这6个版本中微软只推荐使用MSXML2.DOMDocument.6.0或MSXML2.DOMDocument.3.0前者是最新最可靠的版本,而后者是大多数windows操作系统都支持的版本。可以作为后备版本的MSXML2.DOMDocument,仅在IE5.5之前的浏览器开发时才有必要使用。
通过尝试创建每个版本的实例并观察是否有错误发生,可以确定哪个版本可用。例如:
function createDocument(){ if(typeof arguments.callee.activeXString!="string"){//严格模式会报错 var versions=["MSXML2.DOMDocument.6.0","MSXML2.DOMDocument.3.0","MSXML2.DOMDocument"], i, len; for(i=0,len=versions.length;i<len;i++){ try{ new ActiveXObject(versions[i]);//不支持会报错 arguments.callee.activeXString=versions[i]; break; }catch(ex){ //跳过 } } } return new ActiveXObject(arguments.callee.activeXString); }
以上代码是在IE8之前创建适合的XML文档的版本的ActiveX对象的实例,返回一个对象;
要解析XML字符串,首先必须创建一个DOM文档,然后调用loadXML()方法。新创建的XML文档完全是一个空文档,因而不能对其执行任何操作。为loadXML()方法传入的XML字符串经解析之后会填充到DOM文档中:
var xmldom=createDocument(); xmldom.loadXML("<root><child></child></root>"); console.log(xmldom.documentElement.tagName);//"root" console.log(xmldom.documentElement.firstChild.tagName);//"child" var anotherChild=xmldom.createElement("child"); xmldom.documentElement.appendChild(anotherChild); var children=xmldom.getElementsByTagName("child"); console.log(children.length);//2
在新DOM文档中填充了XML内容之后,就可以像操作其它DOM文档一样操作它了(可以使用任何属性和方法);
如果解析过程中出错,可以在parseError属性中找到错误信息。这个属性本身是一个包含多个属性的对象,每个属性都包含着有关解析错误的某一方面信息。
errorCode | 错误类型的数值编码;在没有发生错误时值为0 |
filePos | 文件中导致错误发生的位置 |
line | 发生错误的行 |
linePos | 发生错误的行中的字符 |
reason | 对错误的文本解释 |
srcText | 导致错误的代码 |
url | 导致错误的文件的URL(如果有这个文件的话) |
另外,parseError的valueOf()方法返回errorCode的值,因此可以通过下列代码检测是否发生了解析错误
if(xml.parseError!=0){
console.log("parsing error occurred.");
}
我们可以将这些信息组织起来给出更有价值的解释:
if(xml.parseError!=0){
console.log(
"解析错误:"+ xmldom.parseError.errorCode+"\n"
+ "发生错误的行:"+xmldom.parseError.line+"\n"
+ "发生错误行中的字符:"+xmldom.parseError.linePos+"\n"
+ "错误解释:"+xmldom.parseError.reason
);
}
应该在调用loadXML()之后、查询XML文档之前,检查是否发生了解析错误。
1.序列化XML
IE将序列化XML的能力内置在了DOM文档中。每个DOM节点都有一个xml属性,其中保存着表示该节点的XML字符串。例如:
console.log(xmldom.xml);
2.加载XML文件:(从服务器加载)
IE中的XML文档对象也可以加载来自服务器的文件。与DOM3级中的功能类似,要加载XML文档必须与页面中运行的javascript代码来自同一台服务器。同样与DOM3级规范类似,加载文档的方式也可以分为同步和异步两种。要指定加载文档的方式,可以设置async属性,true表示异步,false表示同步(默认值为true)。例如:
var xmldom=createDocument();
xmldom.async=false;//同步加载
在确定了加载XML文档的方式之后,调用load()启动下载过程,这个方法接收一个参数,即要加载的XML文件的URL。在同步方式下,调用load()后可以立即检测解析错误并执行相关的XML处理,例如:
var xmldom=createDocument();//创建ActiveX对象实例
xmldom.async=false;//同步加载(规定加载方式)
xmldom.load("example.xml");//指定远程URL
if(xmldom.parseError!=0){
//解析错误的处理,比如console出来错误原因
}else{
//对获取来的并解析好的xml文档进行操作
console.log(xmldom.documentElement.tagName);//"root"
console.log(xmldom.documentElement.firstChild.tagName);//"child"
var anotherChild=xmldom.createElement("child");
xmldom.documentElement.appendChild(anotherChild);
var children=xmldom.getElementsByTagName("child");
console.log(children.length);//2
console.log(xmldom.xml);//序列化xml
}
由于是以同步的方式处理XML文件,因此在解析完之前,代码不会继续执行,这样的变成工作要简单一点。虽然同步方式比较方便,但如果下载的时间太长,会导致程序反应很慢,因此在加载XML文档时,通常采用异步的方式。
在异步加载XML文件的情况下,需要XMLDOM文档的onreadystatechange事件指定处理程序。有4个就绪状态(ready state)
1:DOM正在加载数据;
2:DOM已经加载完数据;
3:DOM已经可以使用,但某些部分可能还无法访问。
4:DOM已经加载完成,完全可以使用;
在实际开发中,要关注的只有一个就绪状态:4.这个状态表示XML文件已经全部加载完毕,而且已经全部解析为DOM文档。通过XML文档的readyState属性可以取得其就绪状态。以一部方式加载XML文件的典型模式如下:
var xmldom=createDocument();
xmldom.async=true;
xmldom.onreadystatechange=function(){
if(xmldom.readyState==4){
if(xmldom.parseError!=0){
console.log(
"解析错误:"+ xmldom.parseError.errorCode+"\n"
+ "发生错误的行:"+xmldom.parseError.line+"\n"
+ "发生错误行中的字符:"+xmldom.parseError.linePos+"\n"
+ "错误解释:"+xmldom.parseError.reason
);//最好都是英文
}else{
//操作xmldom文档
}
}
};
xmldom.load("example.xml");//指定远程url
要注意的是:为onreadystatechange事件指定处理程序的语句,必须放在调用load()方法的语句之前;这样才能确保在就绪状态变化时调用该事件梳理程序。另外,在事件处理程序内部还必须注意要使用XML文档变量的名称(xmldom),不能使用this对象。原因是ActiceX控件为为预防安全问题不允许使用this对象。当文档的就绪状态变为4时,就可以放心检测是否发生了解析错误,并在未发生错误的情况下处理XML了。
跨浏览器处理XML:(解析xml)
很少有开发人员能够有福气专门针对一款浏览器做开发。因此,编写能够跨浏览器处理XML的函数就成为了常见的需求。对解析XML而言,下面这个函数可以在所有4种主要浏览器中使用:
function parseXml(xml){
var xmldom=null;
if(typeof DOMParser!="undefined"){//IE高版本和其它浏览器
xmldom=(new DOMParser()).parseFromString(xml,"text/xml");//创建xml文档并解析
var errors=xmldom.getElementsByTagName("parsererror");//检测是否解析错误
if(errors.length){//解析错误处理
throw new Error("XML parsing error:"+errors[0].textContent);
}
}else if(typeof ActiveXObject!="undefined"){//IE8之前版本
xmldom=createDocument();
xmldom.loadXML(xml);
if(xmldom.parseError!=0){//解析错误处理
throw new Error("XML parsing error:"+xmldom.parseError.reason);
}
}else{
throw new Error("NO XML parser available");
}
return xmldom;
}
在使用以上方法解析XML字符串时,应该放在try-catch块儿中,以防发生错误,来看下面的例子:
var xmldom=null; try{ xmldom=parseXml("<root><child></child></root>"); }catch(ex){ console.log(ex.message); } //进一步处理
跨浏览器序列化XML:
function serializeXml(xmldom){
if(typeof XMLSerializer!="undefined"){
return (new XMLSerializer()).serializeToString(xmldom);
}else if(typeof xmldom.xml!="undefined"){//IE8以前版本
return xmldom.xml;
}else {
throw new Error("could not serialize XML DOM");
}
}
浏览器对XPath的支持:
XPath是设计用来在DOM文档中查找节点的一种手段,因而对XML的处理也很重要。但是DOM3级以前的标准并没有就XPath的API作出规定;XPath是在DOM3级XPath模块中首次跻身推荐标准行列的。很多浏览器都实现了这个推荐标准,但IE则以自己的方式实现了XPath。
DOM3级XPath规范定义了再DOM中对XPath表达式求值的接口。要确定某浏览器是否支持DOM3级XPath,可以使用下列javascript代码:
var supportsXPath=document.implementation.hasFeature("XPath","3.0");
console.log(supportsXPath);//true
在DOM3级XPath规范定义的类型中,最重要的两个类型是XPathEvaluator和XPathResult。
XPathEvaluator用于在特定的上下文中对XPath表达式求值。这个类型有下列3个方法。
createExpression(expression,nsresolve): | 将XPath表达式及相应的命名空间信息转换成一个XPathExpression,这是查询的编译版。在多次使用同一个查询时很有用。 |
createNSResolver(node): | 根据node命名空间信息创建一个新的XPathNSResolve对象,在基于使用命名空间的XML文档求值时,需要使用XPathNSResolve对象。 |
evaluate(expression,context,nsresolver,type,result): | 在给定的上下文中,基于特定的命名空间信息来对XPath表达式求值。剩下的参数指定如何返回结果。 |
在firefox、safari、chrome、opera中,document类型通常都是与XPathEvaluator接口一起实现的。换句话说,在这些浏览器中,既可以创建XPathEvaluator的新实例,也可以使用Document实例中的方法(XML或HTML文档均是如此)。
在上面这三个方法中,evaluator()是最常用的。这个方法接收5个参数:XPath表达式、上下文节点、命名空间求解器、返回结果的类型和保存结果的XPathResult对象(通常是null,因为结果也会以函数值的形式返回)。其中,第三个参数(命名空间求解器)只在XML代码中使用了XML命名空间时有必要指定;如果XML代码中没有指定命名空间,则这个参数应该指定为null。第4个参数(返回结果的类型)的取值范围是下列常量之一:
XPathResult.ANY_TYPE: | 返回与XPath表达式匹配的数据类型 |
XPathResult.NUMBER_TYPE: | 返回数值 |
XPathResult.STRING_TYPE: | 返回字符串值 |
XPathResult.BOOLEAN_TYPE: | 返回布尔值 |
XPathResult.UNORDERED_NODE_ITERATOR_TYPE: | 返回匹配的节点集合,集合中节点的次序与他们在文档中的次序不一致 |
XPathResult.ORDERED_NODE_ITERATOR_TYPE: | 返回匹配的节点集合,集合中节点的次序与他们在文档中的次序一致,这是最常用的结果类型 |
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE: | 返回节点集合的快照,由于是在文档外部捕获节点,因此对文档的后续操作不会影响到这个节点的集合,集合中节点的次序不一定与他们在文档中的次序一致 |
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE: | 返回节点集合的快照,由于是在文档外部捕获节点,因此对文档的后续操作不会影响到这个节点的集合,集合中节点的次序与他们在文档中的次序一致 |
XPathResult.ANY_UNORDERED_NODE_TYPE: | 返回匹配的节点集合,但集合中的节点次序不一定与他们在文档中的节点次序一致 |
XPathResult.FIRST_ORDERED_NODE_TYPE: | 返回只包含一个节点的节点集合,包含的这个节点就是文档中第一个匹配的节点 |
指定的结果类型决定了如何取得结果的值。来看一个例子:
var result=xmldom.evaluate("employee/name",xmldom.documentElement,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
if(result!=null){
var element=result.iterateNext();
while(element){
console.log(element.tagName);
node=result.iterateNext();
}
}
上述例子返回最常用的结果类型,返回匹配表达式的结果且次序与文档保持一致。。如果没有节点匹配XPath表达式,evaluate()返回null;否则它返回一个XPathResult对象。这个XPathResult对象带有的属性和方法,可以用来取得特定类型的结果。如果节点是一个节点迭代器,无论是次序一致还是次序不一致,都必须要使用iteratorNext()方法从节点中取得匹配的节点。在没有更多匹配节点时,iteratorNext()返回null;
如果指定的是快照结果类型(不管是次序一致还是次序不一致),就必须使用snapshotItem()方法和snapshotLength属性,例如:
var result=xmldom.evaluate("employee/name",xmldom.documentElement,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null); if(result!==null){ for(var i=0,len=result.snapshotLength;i<len;i++){ console.log(result.snapshotItem()); } }
1.单节点结果:
指定常量XPathResult.FIRST_ORDERED_NODE_TYPE会返回第一个匹配的节点,可以通过结果的singleNodeValue属性来访问该节点。例如:
var result=xmldom.evaluate("employee/name",xmldom.documentElement,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
if(result!==null){
console.log(result.singleNodeValue.tagName);
}
与前面的查询一样,在没有匹配节点的情况下,evaluate()返回null。如果有结果返回,那么久可以通过singleNodeValue来访问它。
2.简单类型结果:
通过XPath也可以取得简单的非节点数据类型,这时候就要使用XPathResult的布尔值、数值、和字符串类型了。这几个结果类型分别会通过booleanValue、numberValue、stringValue属性返回一个值。对于布尔值类型,如果有一个节点与XPath表达式匹配,则求职结果为true,否则返回false。例如:
var result=xmldom.evaluate("employee/name",xmldom.documentElement,null,XPathResult.BOOLEAN_TYPE,null);
console.log(result.booleanValue);
对于数值类型,必须在XPath表达式参数的位置上指定一个能返回数值的XPath函数,例如计算与给定模式匹配的所有节点数量count()。例如:
var result=xmldom.evaluate("count(employee/name)",xmldom.documentElement,null,XPathResult.NUMBER_TYPE,null);
console.log(result.numberValue);
以上代码会输出与“employee/name”匹配的节点数量(即2)。如果使用这个方法的时候没有指定与前例类似的XPath函数,那么numberValue的值将等于NaN.
对于字符串类型,evaluate()方法会查找与XPath表达式匹配的第一个节点,然后返回其第一个子节点的值(实际上是假设第一个子节点为文本节点)。如果没有匹配的节点,结果就是一个空字符串。例如:
var result=xmldom.evaluate("employee/name",xmldom.documentElement,null,XPathResult.STRING_TYPE,null);
console.log(result.stringValue);
3.默认结果类型:
所有XPath表达式都会自动映射到特定的结果类型。像前面那样设置特定的结果类型,可以限制表达式的输出。而使用XPathResult.ANY_TYPE常量可以自动确定返回结果的类型。一般来说,自动选择的结果类型可能是布尔值、数值、字符串、或一个次序不一致的节点迭代器。要确定返回的是什么结果类型,可以检测结果的resultType属性,如下面的例子所示。
var result=xmldom.evaluate("employee/name",xmldom.documentElement,null,XPathResult.ANY_TYPE,null);
if(result !==null){
switch(result.resultType){
case XPathResult.STRING_TYPE:
//处理字符串类型
break;
case XPathResult.NUMBER_TYPE:
//处理数值类型
break;
case XPathResult.BOOLEAN_TYPE:
//处理布尔值类型
break;
case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
//处理次序不一致的节点迭代器类型
break;
default:
//处理其他可能的结果类型
}
}
显然,XPathResult.ANY_TYPE可以让我们更灵活地使用XPath,但是却要求有更多的处理代码来处理返回的结果。
4.命名空间的支持:
对于利用了命名空间的XML文档,XPathEvaluator必须知道命名空间信息,然后才能正确地进行求值。处理命名空间的方法也不止一种。我们以下面的XML代码为例:
<?xml version="1.0" ?> <wrox:books xmlns:wrox="http://www.wrox.com/"> <wrox:book> <wrox:title>professionnal Javascript for web Developers</wrox:title> <wrox:author>Nicholas C. Zakas</wrox:author> </wrox:book> <wrox:book> <wrox:title>professionnal Ajax</wrox:title> <wrox:author>Nicholas C. Zakas</wrox:author> <wrox:author>Jeremy Mcpeak</wrox:author> <wrox:author>Joe Fawcett</wrox:author> </wrox:book> </wrox:books>
正在这个XML文档中,所有的元素定义都来自http://www.wrox.com/命名空间,以前缀wrox标识。如果要对这个文档使用XPath,就需要定义要使用的命名空间;否则求值将会失败。
处理命名空间的第一种方法是通过createNSResolver()来创建XPathNSResolver对象,这个方法接收一个参数,即文档中包含命名空间定义的节点。对于前面的XML文档来说,这个节点就是文档元素<wrox:books>,它的xmlns特性定义了命名空间。可以把这个节点传递给createNSResolver(),然后可以像下面这样在evaluate()中使用返回的结果:
var nsresolver=xmldom.createNSResolver(xmldom.documentElement);//XML的文档元素
var result=xmldom.evaluate("wrox:book/wrox:author",xmldom.documentElement,nsresolver,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
console.log(result.snapshotLength);
在将nsresolver对象传入到evaluate()之后,就可以确保它能够理解XPath表达式中使用的wrox前缀。读者可以试一试使用相同的表达式,如果不使用XPathNSResolver的话,就会导致错误。
处理命名空间的第二种方法就是定义一个函数,让它接收一个命名空间前缀,返回关联的URI,例如:
var nsresolver=function(prefix){
switch(prefix){
case "wrox": return "http://www.wrox.com";
//其它前缀
}
}
var result=xmldom.evaluate("count(wrox:book/wrox:author)",xmldom.documentElement,nsresolver,XPathResult.NUMBER_TYPE,null);
console.log(result.numberValue);
在不确定文档中的哪个节点包含命名空间定义的情况下,这个命名空间解析函数就可以派上用场了。只要你知道前缀和URI,就可以定义一个返回该信息的函数,然后将它作为第三个参数传递给evaluate()即可。
IE中的XPath:
IE对XPath的支持是内置在基于ActiveX的XML DOM文档对象中的,没有使用DOMParser返回的DOM对象。因此,为了再IE9及之前版本中使用XPath,必须使用基于ActiveX的实现。这个借口在每个节点上额外定义了两个方法:selectSingleNode()和selectNodes()。其中,selectSingleNode()方法接收一个XPath模式,在找到匹配节点时返回第一个匹配的的节点,如果没有找到匹配的节点就返回null。例如:
var element=xmldom.documentElement.selectSingleNode("employee/name");
if(element!=null){
console.log(element.xml);//序列化
}
这里,会返回匹配“employee/name”的第一个节点。上下文节点是xmldom.documentElement,因此就调用了该节点的selectSingleNode()。
另一个方法selectNodes()也接收一个XPath模式作为参数,但他返回与模式匹配的所有节点的NodeList(如果没有匹配的节点,则返回一个包含0项的NodeList)。例如:
var elements=xmldom.documentElement.selectNodes("employee/name");
console.log(elements.length);
无论有没有匹配的节点都会返回NodeList,但是其包含的元素可能会动态改变,所以每次访问它都有可能得到不同的结果。
IE对命名空间的支持:
要在IE中处理包含命名空间的XPath表达式,你必须知道自己使用的命名空间,并按照下列格式创建一个字符串:
"xmlns:prefix1='uri1' xmlns:prefix2='uri2' xmlns:prefix3='uri3'"
然后,必须将这个字符串传入到XML DOM文档对象的特殊方法setProperty()中,这个方法接收两个参数:要设置的属性名和属性值。在这里,属性名应该是"SelectionNameSpaces",属性值就是按照前面的格式创建的字符串。下面来看一个在DOM Xpath命名空间中对XML文档求值的例子:
xmldom.setproperty("selectionNameSpaces","xmlns:wrox='http://www.wrox.com/'");
var result=xmldom.documentElement.selectNodes("wrox:book/wrox:author");
console.log(result.length);
对于这个DOM XPath的例子来说,如果不提供命名空间解析信息,就会在对表达式求值时导致一个错误。
跨浏览器使用XPath:
鉴于IE对XPath功能的支持有限,因此跨浏览器XPath只能保证达到IE支持的功能。换句话说,也就是要在其它使用DOM3级XPath对象的浏览器中,重新创建selectSingleNode()和selectNodes()方法。第一个函数是selectSingleNode(),它接收三个参数:上下文节点、XPath表达式和可选的命名空间对象。命名空间对象应该是下面这种字面量形式:
var namespaces={
prefix1:"uri1",
prefix2:"uri2",
prefix3:"uri3"
}
以这种方式提供的命名空间信息,可以方便的转换为针对特定浏览器的命名空间解析格式。下面给出了selectSingleNode()函数的完整代码:
function selectSingleNode(context,expression,namespaces){
var doc=(context.nodeType!=9?context.ownerDocument:context);//确定文档类型是XML
if(typeof doc.evaluate!="undefined"){//确定有evaluate函数
var nsresolver=null;
if(namespaces instanceof Object){//确定namespaces是object类型
nsresolver=function(prefix){
return namespaces[prefix];
}
}
var result = doc.evaluate(expression,context,nsresolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
return (result!=null?result.singleNodeValue:null);
}else if(typeof context.selectSingleNode!="undefined"){//IE9之前
//创建命名空间字符串
if(namespaces instanceof Object){//确定namespaces是object类型
var ns="";
for(var prefix in namespaces){
if(namespaces.hasOwnProperty(prefix)){
ns+="xmlns:"+prefix+"='"+namespaces[prefix]+"'";
}
}
doc.setproperty("SelectionNamespaces",ns);
}
return context.selectSingleNode(expression);
}else{
throw new Error("No XPath engine found.");
}
}
以下是使用selectSingleNode()函数的事例:
var result=selectSingleNode(xmldom.documentElement,"wrox:book/wrox:author",{wrox:"http://www.wrox.com/"});
console.log(serializeXml(result));//将xml转化为dom
类似的,也可以创建一个跨浏览器的selectNodes()函数,这个函数同样接收三个参数:上下文节点、XPath表达式、可选的命名空间对象;
function selectNodes(context,expression,namespaces){ var doc=(context.nodeType!=9?context.ownerDocument:context); if(typeof doc.evaluate!="undefined"){ var nsresolver=null; if(namespaces instanceof Object){ nsresolver=function(prefix){ return namespaces[prefix]; } } var result=doc.evaluate(expression,context,nsresolver,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null); var nodes=new Array(); if(result!==null){ for(var i=0,len=result.snapshotLength;i<len;i++){ nodes.push(result.snapshotItem(i)); } } return nodes; }else if(typeof context.selectNodes!="undefined"){ //创建命名空间字符串 if(namespaces instanceof Object){ var ns=""; for(var prefix in namespaces){ if(namespaces.hasOwnProperty(prefix)){ ns+="xmlns:"+prefix+"='"+namespaces[prefix]+"' "; } } doc.setproperty("SelectionNamespaces",ns); } var result=context.selectNodes(expression); var nodes=new Array(); for(var i=0,len=result.length;i<len;i++){ nodes.push(result[i]); } return nodes; }else{ throw new Error("No XPath engine found."); } }
使用事例:
var result=selectNodes(xmldom.documentElement,"wrox:book/wrox:author",{wrox:"http://www.wrox.com"});
console.log(result.length);
为了求得最佳的浏览器兼容性,建议再javascript中使用XPath时,只考虑着两个方法。
浏览器对XSLT的支持:
先来了解一下什么是XSLT,在了解XSLT之前,的先了解XSL,xsl是xml的样式表,可以比喻成css是html的样式表,只是使用起来有些不同,而XSLT,则用来将xml装换成不同格式的xml文档,比如XHTML、HTML等。(具体可以上w3cschool来具体了解,讲的很详细,而且很快可以了解一遍)。
DOM中并没有实现XSLT,只有浏览器开发商来实现,不过也可以通过javascript来使用XSLT.
1.简单的XSLT转换
使用XSLT样式表转换XML文档的最简单的方式,就是将它们分别加到一个DOM文档中,然后再使用transformNode()方法。这个方法存在与文档的所有节点中,它接受一个参数,即包含XSLT样式表的文档。调用transformNode()方法会返回一个包含你转换信息的字符串。例如:
//加载XML和XSLT(仅限于IE)
xmldom.load("employees.xml");
xsltdom.load("employees.xslt");
//转换
var result = xmldom.transformNode(xsltdom);
以上例子加载了一个XML的DOM文档和一个XSLT样式表的DOM文档。然后在XML文档节点上调用了transformNode()方法,并传入XSLT。变量result中最后就会保存一个转换之后得到的字符串。需要注意的是,由于是在文档节点上调用的transformNode(),因此转换是从文档节点开始的。实际上,XSLT转换可以在文档的任何级别上进行,只要在想要开始转换的节点上调用transformNode()方法即可。比如:
result=xmldom.documentElement.transformNode(xsltdom);
result=xmldom.documentElement.childNodes[1].transformNode(xsltdom);
result=xmldom.getElementsByTagName("name")[0].transformNode(xsltdom);
result=xmldom.documentElement.firstChild.lastChild.transformNode(xsltdom);
2.复杂的XSLT转换
当使用这种语言的更复杂的方式时,必须要使用XSL模板和XSL处理器。第一步要把XSLT样式表加载到一个线程安全的XML文档中。而这可以通过使用Active对象MSXML2.FreeThreadedDocument来做到。这个ActiveX对象与IE中常规的DOM支持相同的接口。此外,创建这个对象时应该尽可能使用最新版本。例如:
略略略。。。看w3c更详细。。。
3.跨浏览器使用XSLT
只能返回结果是字符串
function transform(context,xslt){
if(typeof XSLTProcessor!="undefined"){
var processor=new XSLTProcessor();
processor.importStylesheet(xslt);
var result=processor.transformToDocument(context);
return (new XMLSerializer()).serializeToString(result);
}else if(typeof context.transformNode!="undefined"){
return context.transformNode(context);
}else {
throw new Error("No XSLT processor available");
}
}
这个方法接收两个参数,上下文节点和XSLT文档对象