AJAX技术入门 第四节 DOM与XML

1.HTML 和 XML 的DOM

HTML的DOM中我们提到并大量使用了document这个Javascirpt的内置对象,请注意这个对象仅仅可以表示HTML的根节点。

而对于XML的DOM来说,每一个XML都一个根节点。

如果我们想建立一个节点的时候,也要使用XML的根节点来创建,这样才能保证新建的节点可以被添加到XML中,从而改变XML的内容。

35

2.利用浏览器装载 XML文件或者XML字符串

代码测试:

通用的Javascript代码:

/**
 * 封装IE和FF类浏览器装载同域的xml文件或者xml字符串的方法
 * @param flag true表示装载xml文件,false表示装载xml字符串
 * @param xmldoc flag为true时表示xml文件的位置,flag为false时表示xml的字符串
 * @return 根元素节点
 */
function loadXML(flag, xmldoc){
    var xmlObj;
    if (window.ActiveXObject) {//IE
        var activeName = ["MSXML2.DOMDocument", "Microsoft.XmlDom"];
        for (var i = 0; i < activeName.length; i++) { //生成ActiveXObject
            try {
                xmlObj = new ActiveXObject(activeName[i]);
                break;
            } 
            catch (e) {
            }
        }
        if (xmlObj) {
            xmlObj.async = false;//同步装载
            if (flag) {//xml文件
                xmlObj.load(xmldoc);
            }
            else {//xml字符串
                xmlObj.loadXML(xmldoc);
            }
            return xmlObj.documentElement;
        }
        else {
            alert("装载XML时出错!");
            return null;
        }
    }
    else if (document.implementation.createDocument) {//FF
        if (flag) {//xml文件
            xmlObj = document.implementation.createDocument("", "", null);
            if (xmlObj) {
                xmlObj.async = false; //同步装载
                xmlObj.load(xmldoc);
                return xmlObj.documentElement;
            }
            else {
                alert("装载XML时出错!");
                return null;
            }
        }
        else {//xml字符串
            xmlObj = new DOMParser();
            var root = xmlObj.parseFromString(xmldoc, "text/xml");
            return root.documentElement;
        }
    }
    alert("装载XML时出错!");
    return null;
}

测试页面可以在下面的第二张图片中查看

在 IE 中测试

33

在 Firefox 中测试

34

DOM除了可以解析和修改XML以外,还可以从字符串或xml文件中加载XML,或者新建一个空的XML

在IE的装载过程中,仍然使用了ActiveX对象来做工作 ,新建了对象以后还需要设置使用同步方式装载文档,这和我们使用XMLHttpRequest的方式不同,

当然如果你需要, 也可以异步的装载文档,这个时候就需要像XMLHttpRequest对象一样设置onreadystatechange属性了。

IE中装载XML的对象提供了两个方法分别用于装载XML文件和包含XML的字符串。最后我们返回的是XML文档的根元素节点。

如果在实际应用中需要返回根节点,只要将return的内容从xmlDoc.documentElement变成xmlDoc就可以了。

在FireFox的装载过程中,装载XML文件和装载XML字符串是不一样的。

如果需要装载XML文件,应该首先通过document.implementation.createDocument建立一个空文档,然后调用文档对象的load方法来装载。

而如果要装载XML字符串,则是需要建立一个DOMParser的对象,然后通过parseFromString方法进行装载,返回文档对象。

需要注意的是在FireFox中,只有装载XML文件才会需要设置同步方式装载文档,如果需要像XMLHttpRequest一样异步处理,就需要设置onload属性,

而且FireFox中处理装载完成的情况和IE也不一样,不是XMLHttpRequest的那种需要判断readyState的方式,而是直接会执行onload属性对应的方法中的内容。

最后还有一点要说明的是,在IE和FireFox中装载XML文件时,会有和XMLHttpRequest对象一样的安全性限制,也就是不能访问跨域的XML数据。

由于通常情况下不会装载跨域的XML数据,因此这个问题不是很麻烦,如果却是有需要装载跨域XML数据,

那么可以考虑使用前面解决XMLHttpReuqest对象跨域问题的思路来解决这个问题。

3. 利用DOM API 操作 XML文件

测试代码包含在下面的测试中

4. 解决 空白信息 的问题

什么是空白信息呢?

①对于HTML来说,body的子孙节点中(请注意,并不是HTML页面中的所有位置都有这个空白信息的概念),如果两个元素节点之间有空格,

回车这样的不包含任何文字信息的内容,那这些信息就都是空白信息(注意如果一段文字信息之中包含空格,回车,制表符,则这些不算是空白信息)

②对于XML来说根节点的子孙节点中,如果两个元素节点之间有空格,回车这样的不包含任何文字信息的内容,那这些信息就都是空白信息。

在IE中,这些空白信息很没有地位

①IE的DOM实现在解析HTML时,如果两个元素节点之间全是空白信息,则都会被过滤掉,如果两个元素节点之间既有空白信息又有文本信息,

则文字信息之前的空白信息都会被过滤掉,文本信息之后的空白信息中空格制表符会被保留,换行符则被过滤

②而IE的DOM实现在解析XML时,如果两个元素节点之间全是空白信息,则都会被过滤掉,如果两个元素节点之间既有空白信息又有文本信息,

则空白信息都会被保留。

在FireFox中,这些空白信息则基本被一视同仁

①FireFox的DOM实现在解析HTML时,如果两个元素节点之间全是空白信息,则都会被保留,如果两个元素节点之间既有空白信息又有文本信息,

则文字信息之前的空白信息都会被过保留,文本信息之后的空白信息则都被过滤

②FireFox的DOM实现在解析XML时会把这些空白信息都保留。

解决方法:

由于两个元素节点之间既有空白信息又有文本信息时我们无法确定其中的空白信息是否无用,因此我们只针对两个元素节点之前全部是空白信息的情况进行处理。

function removeBlank(xml){
    if (xml.childNodes.length > 1) {
        for (var loopIndex = 0; loopIndex < xml.childNodes.length; loopIndex++) {
            var currentNode = xml.childNodes[loopIndex];
            if (currentNode.nodeType == 1) {
                removeBlank(currentNode);
            }
            if (((/^\s+$/.test(currentNode.nodeValue))) && (currentNode.nodeType == 3)) {
                xml.removeChild(xml.childNodes[loopIndex--]);
            }
        }
    }
}

5.将DOM对象序列化成XML字符串

36

通用方法:

function serializeDOM(xmldoc){//将DOM对象序列化成XML字符串
    if (xmldoc.xml) {//IE
        return xmldoc.xml;
    }
    else if (window.XMLSerializer) {//FF
        var seria = new XMLSerializer();
        return seria.serializeToString(xmldoc);
    }
    return null;
}

第3,4,5小节的测试代码:

xml.js

/**
 * 与操作XML有关的js方法
 */

function serializeDOM(xmldoc){//将DOM对象序列化成XML字符串
    if (xmldoc.xml) {//IE
        return xmldoc.xml;
    }
    else if (window.XMLSerializer) {//FF
        var seria = new XMLSerializer();
        return seria.serializeToString(xmldoc);
    }
    return null;
}

function removeBlank(xml){//移除空白信息
    if (xml.childNodes.length > 1) {//深度优先搜索
        for (var loopIndex = 0; loopIndex < xml.childNodes.length; loopIndex++) {
            var currentNode = xml.childNodes[loopIndex];
            if (currentNode.nodeType == 1) {//元素节点
                removeBlank(currentNode);
            }
            if (((/^\s+$/.test(currentNode.nodeValue))) && (currentNode.nodeType == 3)) {//包含空白信息的文本节点
                xml.removeChild(xml.childNodes[loopIndex--]);
            }
        }
    }
}
/**
 * 封装IE和FF类浏览器装载同域的xml文件或者xml字符串的方法
 * @param flag true表示装载xml文件,false表示装载xml字符串
 * @param xmldoc flag为true时表示xml文件的位置,flag为false时表示xml的字符串
 * @return 根元素节点
 */
function loadXML(flag, xmldoc){
    var xmlObj;
    if (window.ActiveXObject) {//IE
        var activeName = ["MSXML2.DOMDocument", "Microsoft.XmlDom"];
        for (var i = 0; i < activeName.length; i++) { //生成ActiveXObject
            try {
                xmlObj = new ActiveXObject(activeName[i]);
                break;
            } 
            catch (e) {
            }
        }
        if (xmlObj) {
            xmlObj.async = false;//同步装载
            if (flag) {//xml文件
                xmlObj.load(xmldoc);
            }
            else {//xml字符串
                xmlObj.loadXML(xmldoc);
            }
            return xmlObj.documentElement;
        }
        else {
            alert("装载XML时出错!");
            return null;
        }
    }
    else if (document.implementation.createDocument) {//FF
        if (flag) {//xml文件
            xmlObj = document.implementation.createDocument("", "", null);
            if (xmlObj) {
                xmlObj.async = false; //同步装载
                xmlObj.load(xmldoc);
                return xmlObj.documentElement;
            }
            else {
                alert("装载XML时出错!");
                return null;
            }
        }
        else {//xml字符串
            xmlObj = new DOMParser();
            var root = xmlObj.parseFromString(xmldoc, "text/xml");
            return root.documentElement;
        }
    }
    alert("装载XML时出错!");
    return null;
}

测试用的XML文件:test.xml

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book>aaa</book>
    <book>bbb</book>
</books>

测试页面:xml.html

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <script type="text/javascript" src="js/xml.js">
        </script>
        <script type="text/javascript">
            function test(){
                var xml1 = loadXML(true, "test.xml");
                alert(serializeDOM(xml1));
                removeBlank(xml1);
                alert(serializeDOM(xml1));
                var root = xml1.parentNode;//注意,一定要先取得根节点,通过根节点来创建其他节点
                var node = root.createElement("book");
                var text = root.createTextNode("abc");
                node.appendChild(text);
                xml1.appendChild(node);
                alert(serializeDOM(xml1));
                
//                var xml2 = loadXML(false, "<books><book>aaa</book></books>");
//                alert(serializeDOM(xml2));
//                removeBlank(xml2);
//                alert(serializeDOM(xml2));
//                var root = xml2.parentNode;
//                var node = root.createElement("book");
//                var text = root.createTextNode("abc");
//                node.appendChild(text);
//                xml2.appendChild(node);
//                alert(serializeDOM(xml2));

                //alert(xml2.lastChild.childNodes[0].nodeValue);
            }
        </script>
    </head>
    <body>
        <input type="button" value="Test" onclick="test()">
        <br/>
    </body>
</html>

在FF中的测试结果:三次弹出框的结果

37

38

39

6.XPATH

问题:getElementById方法在解析XML的时候是否适合呢?

首先XML中每一个元素节点不一定有id属性

其次XML中的两个元素节点可能有相同的id属性,这样getElementById这个方法就不再能保证找到我们需要的唯一节点了

因此这种方式并不适合XML的解析。

XPATH是什么?全称是XML Path Language(XML路径语言),适用于确定XML文档中某节点位置的语言。

我们可以把它当作类似于SQL一样的查询语言,通过给出XPTAH路径信息(就像SQL命令一样)就可以从XML中

查找出符合条件的节点(就像从数据库中返回需要的数据一样)。

使用过DOM4j的人可能都知道可以利用其中的selectSingleNode和selectNodes 方法直接给出XPATH地址来获取任意深度的一个或多个节点。

在IE6.0及其以后版本中我们可以使用同样的方式来访问任意深度的XML数据,这给XML数据的解析操作带来了便利。

但是在FireFox等浏览器中,则是使用了w3c标准的XPATH处理方式,没有IE这么简单的方式。

测试用的xml:book.xml

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book isbn="0001">
        <author>
            Wang
        </author>
        <name>
            AJAX Professional
        </name>
        <price>
            35
        </price>
    </book>
    <book isbn="0002">
        <author>
            Lee
        </author>
        <name>
            AJAX In Action
        </name>
        <price>
            65
        </price>
    </book>
    <book>
        <author>
            Zhu
        </author>
        <name>
            AJAX For Dummies
        </name>
        <price>
            40
        </price>
    </book>
</books>

【1】所有的author节点

【2】获取所有isdn属性节点

【3】获取所有name节点中的文本内容

【4】有isdn属性的book节点

【5】获得子节点price值大于40且isdn等于0002的所有book节点

【6】获得子节点price值大于35或isdn等于0002的所有book节点

查找元素节点:

如果当前节点是根节点:“book/author”

不管当前节点是什么都可以用:”/books/book/author”或”//author”或”//book/author”

XPATH的路径即可以是相对路径,也可以是绝对路径。

当以”/”开头,则表示绝对路径方式,从根节点开始查找

如果不以”/”开头,则表示相对路径,这个时候相对的是我们前面的封装函数中的第一个参数所表示的节点对象。注意,

如果当前节点是某一个book节点,我们只能通过”author”找到这个book节点下的author节点。

如果以”//”开头,则直接找整个文档中符合条件的节点,不管这个节点在哪里,找到所有的author节点。

查找属性节点:

如果当前节点是根节点:“book/@isdn”

不管当前节点是什么都可以用:”/books/book/@isdn”或”//@isdn”或“//book/@isdn

获取属性节点和获取元素节点在路径表示上是一样的,即可以用相对路径,也可以用绝对路径,还可以用”//”查找任意满足的节点。

不同之处是对于元素节点,直接用节点标签名表示,而属性节点则用@加上属性名来表示。

查找文本节点:

如果当前节点是根节点:“book/name/text()”

不管当前节点是什么都可以用:”/books/book/name/text()”或”//name/text()”或”//book/ name/text()”

获取文本节点和前面两个在路径表示上也是一样的。

不同之处在于需要使用text()方法

多条件的查找:

如果当前节点是根节点:“book[@isdn=’0002’][price>35]”

不管当前节点是什么都可以用:”/books/book[@isdn=’0002’][price>40]”或”//book[@isdn=’0002’][price>40]”

在XPATH中可以在[]中指定条件,从而只获得满足某些条件的节点

比如需求(4)中我们期望获得包含isdn属性的book节点,就可以用[@isdn]来表示

对于需求(5),我们期望isdn属性是0002,而且book的price子节点的值大于40,对于前一个条件,可以使用[@isdn=’0002’],

对于后一个条件可以使用[price>40],要同时满足两个条件,只要把这两个都写上就可以了。

如果当前节点是根节点:“book[@isdn=’0002’] | book[price>35]”

不管当前节点是什么都可以用:”/books/book[@isdn=’0002’] | /books/book[price>35]”或”//book[@isdn=’0002’] | //book[price>35]”

和(5)的需求不同之处是我们需要的不是与关系,而是或关系,这个时候我们可以使用”|”表示或关系,”|”可以同时连接多个XPATH路径。

这里需要注意的是这样返回的节点数组是不会有重复信息的,例如我们这个例子中price>35的节点有两个,其中一个满足isdn=’0002’这个条件,

最后的返回结果中只有两个节点,而不会有三个节点。

通用的解决方法:

添加的js方法:

//根据指定的节点和path查找第一个节点
function selectSingleNode(xmldoc,xpath){
    if(window.ActiveXObject){
        return xmldoc.selectSingleNode(xpath);
    }
    else if(window.XPathEvaluator){
        var xpe = new XPathEvaluator();
        if(xpe){
            var result=xpe.evaluate(xpath,xmldoc,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
            return result.singleNodeValue;
        }else{
            return null;
        }
    }else{
        return null;
    }
}

//根据指定的节点和path查找所有的节点
function selectNodes(xmldoc,xpath){
    if(window.ActiveXObject){
        return xmldoc.selectNodes(xpath);
    }
    else if(window.XPathEvaluator){
        var xpe = new XPathEvaluator();
        if(xpe){
            var result=xpe.evaluate(xpath,xmldoc,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
            var nodes = new Array();
            var node;
            while((node=result.iterateNext())!=null){
                nodes.push(node);
            }
            return nodes;
        }else{
            return null;
        }
    }else{
        return null;
    }
}

测试用的html代码:

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <script type="text/javascript" src="js/xml.js">
        </script>
        <script type="text/javascript">
            function test(){
                //查找元素节点
                var rootElement = loadXML(true,"book.xml");
                removeBlank(rootElement);
                var author1 = selectNodes(rootElement,"/books/book/author");//绝对路径查找
                var author2 = selectNodes(rootElement,"book/author");//相对路径查找
                var author3 = selectNodes(rootElement,"//author");//全文档查找
                var author4 = selectNodes(rootElement,"//book/author");//全文档相对路径查找

                //查找属性节点,首先要找到元素节点
                var isbn1 = selectNodes(rootElement,"/books/book/@isbn");//绝对路径查找
                var isbn2 = selectNodes(rootElement,"book/@isbn");//相对路径查找
                var isbn3 = selectNodes(rootElement,"//@isbn");//全文档查找
                var isbn4 = selectNodes(rootElement,"//book/@isbn");//全文档相对路径查找 
            
                //查找文本节点,首先要找到元素节点
                var text1 = selectNodes(rootElement,"/books/book/text()");//绝对路径查找
                var text2 = selectNodes(rootElement,"book/text()");//相对路径查找
                var text3 = selectNodes(rootElement,"//text()");//全文档查找
                var text4 = selectNodes(rootElement,"//book/text()");//全文档相对路径查找 
                
                //有条件的查找元素节点
                var book1 = selectNodes(rootElement,"/books/book[@isbn]");//含有isbn的book
                var book2 = selectNodes(rootElement,"book[@isbn]");//相对路径查找
                var book3 = selectNodes(rootElement,"//book[@isbn]");//全文档相对路径查找 
                
                //多条件的查找
                var book4 = selectNodes(rootElement,"/books/book[@isbn='0002'] | /books/book[price>40]");//或
                var book5 = selectNodes(rootElement,"/books/book[@isbn='0002'][price>40]");//且
                var book6 = selectNodes(rootElement,"/books/book[@isbn='0001'] | /books/book[price>30]");//或
                var book7 = selectNodes(rootElement,"/books/book[@isbn='0001'][price=35]");//且

                alert("");
            }
        </script>
    </head>
    <body>
        <input type="button" value="Test" onclick="test()">
        <br/>
    </body>
</html>

测试结果大家自己去试试看吧,呵呵,这里就不贴出来了!本节结束!

XPATH的详细的实例教程:http://www.blogjava.net/supercrsky/articles/196011.html

原文地址:https://www.cnblogs.com/yinger/p/2159026.html