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文档对象