posts - 73,  comments - 55,  trackbacks - 0
< script language = " JavaScript " >
<!--
var  doc  =   new  ActiveXObject( " Msxml2.DOMDocument " );  // ie5.5+,CreateObject("Microsoft.XMLDOM") 


// 加载文档
//
doc.load("b.xml");

// 创建文件头
var  p  =  doc.createProcessingInstruction( " xml " , " version='1.0'  encoding='gb2312' " );

    
// 添加文件头

    doc.appendChild(p);

// 用于直接加载时获得根接点

//
var root = doc.documentElement;

// 两种方式创建根接点
//
    var root = doc.createElement("students");
     var  root  =  doc.createNode( 1 , " students " , "" );

    
// 创建子接点

     var  n  =  doc.createNode( 1 , " ttyp " , "" );

        
// 指定子接点文本

         // n.text = " this is a test";
    
    
// 创建孙接点

     var  o  =  doc.createElement( " sex " );
        o.text 
=   " " ;     // 指定其文本


    
// 创建属性
     var  r  =  doc.createAttribute( " id " );
        r.value
= " test "
;

        
// 添加属性

        n.setAttributeNode(r);

    
// 创建第二个属性    

     var  r1  =  doc.createAttribute( " class " );
        r1.value
= " tt "
;
        
        
// 添加属性

        n.setAttributeNode(r1);

        
// 删除第二个属性

        n.removeAttribute( " class " );

        
// 添加孙接点

        n.appendChild(o);

        
// 添加文本接点

        n.appendChild(doc.createTextNode( " this is a text node. " ));

        
// 添加注释

        n.appendChild(doc.createComment( " this is a comment\n " ));
    
        
// 添加子接点

        root.appendChild(n);
    
    
// 复制接点

     var  m  =  n.cloneNode( true );

        root.appendChild(m);
        
        
// 删除接点

        root.removeChild(root.childNodes( 0 ));

    
// 创建数据段

     var  c  =  doc.createCDATASection( " this is a cdata " );
        c.text 
=   " hi,cdata "
;
        
// 添加数据段

        root.appendChild(c);
    
    
// 添加根接点

    doc.appendChild(root);

    
// 查找接点

     var  a  =  doc.getElementsByTagName( " ttyp " );
    
// var a = doc.selectNodes("//ttyp");


    
// 显示改接点的属性
     for ( var  i =   0 ;i < a.length;i ++ )
    
{
        alert(a[i].xml);
        
for ( var  j = 0 ;j < a[i].attributes.length;j ++
)
        
{
            alert(a[i].attributes[j].name);
        }

    }


    
// 修改节点,利用XPATH定位节点
     var  b  =  doc.selectSingleNode( " //ttyp/sex " );
    b.text 
=   " "
;

    
// alert(doc.xml);


    
// XML保存(需要在服务端,客户端用FSO)
     // doc.save();
    
    
// 查看根接点XML

     if (n)
    
{
        alert(n.ownerDocument.xml);
    }


// -->
</ script >


    在
DOM 眼中, HTML XML 一样是一种树形结构的文档, <html> 是根( root )节点, <head> <title > <body> <html> 的子( children )节点,互相之间是兄弟( sibling )节点; <body> 下面才是子节点 <table> <span> <p> 等等。如下图:
HTML文档结构.jpg
    这个是不是跟
XML 的结构有点相似呢。不同的是, HTML 文档的树形主要包含表示元素、标记的节点和表示文本串的节点。


 HTML
文档的节点

DOM 下, HTML 文档各个节点被视为各种类型的 Node 对象。每个 Node 对象都有自己的属性和方法,利用这些属性和方法可以遍历整个文档树。由于 HTML 文档的复杂性, DOM 定义了 nodeType 来表示节点的类型。这里列出 Node 常用的几种节点类型:

接口

nodeType 常量

nodeType

备注

Element

Node.ELEMENT_NODE

1

元素节点

Text

Node.TEXT_NODE

3

文本节点

Document

Node.DOCUMENT_NODE

9

document

Comment

Node.COMMENT_NODE

8

注释的文本

DocumentFragment

Node.DOCUMENT_FRAGMENT_NODE

11

document 片断

Attr

Node.ATTRIBUTE_NODE

2

节点属性

DOM 树的根节点是个 Document 对象,该对象的 documentElement 属性引用表示文档根元素的 Element 对象(对于 HTML 文档,这个就是 <html> 标记)。 Javascript 操作 HTML 文档的时候, document 即指向整个文档, <body> <table> 等节点类型即为 Element Comment 类型的节点则是指文档的注释。具体节点类型的含义,请参考《 Javascript 权威指南》,在此不赘述。

Document 定义的方法大多数是生产型方法,主要用于创建可以插入文档中的各种类型的节点。常用的 Document 方法有:

方法

描述

createAttribute()

用指定的名字创建新的 Attr 节点。

createComment()

用指定的字符串创建新的 Comment 节点。

createElement()

用指定的标记名创建新的 Element 节点。

createTextNode()

用指定的文本创建新的 TextNode 节点。

getElementById()

返回文档中具有指定 id 属性的 Element 节点。

getElementsByTagName()

返回文档中具有指定标记名的所有 Element 节点。

对于 Element 节点,可以通过调用 getAttribute() setAttribute() removeAttribute() 方法来查询、设置或者删除一个 Element 节点的性质,比如 <table> 标记的 border 属性。下面列出 Element 常用的属性:

属性

描述

tagName

元素的标记名称,比如 <p> 元素为 P HTML 文档返回的 tabName 均为大写。

Element 常用的方法:

方法

描述

getAttribute()

以字符串形式返回指定属性的值。

getAttributeNode()

Attr 节点的形式返回指定属性的值。

getElementsByTabName()

返回一个 Node 数组,包含具有指定标记名的所有 Element 节点的子孙节点,其顺序为在文档中出现的顺序。

hasAttribute()

如果该元素具有指定名字的属性,则返回 true

removeAttribute()

从元素中删除指定的属性。

removeAttributeNode()

从元素的属性列表中删除指定的 Attr 节点。

setAttribute()

把指定的属性设置为指定的字符串值,如果该属性不存在则添加一个新属性。

setAttributeNode()

把指定的 Attr 节点添加到该元素的属性列表中。

Attr 对象代表文档元素的属性,有 name value 等属性,可以通过 Node 接口的 attributes 属性或者调用 Element 接口的 getAttributeNode() 方法来获取。不过,在大多数情况下,使用 Element 元素属性的最简单方法是 getAttribute() setAttribute() 两个方法,而不是 Attr 对象。


使用
DOM 操作 HTML 文档

Node 对象定义了一系列属性和方法,来方便遍历整个文档。用 parentNode 属性和 childNodes[] 数组可以在文档树中上下移动;通过遍历 childNodes[] 数组或者使用 firstChild nextSibling 属性进行循环操作,也可以使用 lastChild previousSibling 进行逆向循环操作,也可以枚举指定节点的子节点。而调用 appendChild() insertBefore() removeChild() replaceChild() 方法可以改变一个节点的子节点从而改变文档树。

需要指出的是, childNodes[] 的值实际上是一个 NodeList 对象。因此,可以通过遍历 childNodes[] 数组的每个元素,来枚举一个给定节点的所有子节点;通过递归,可以枚举树中的所有节点。下表列出了 Node 对象的一些常用属性和方法:

Node 对象常用属性:

属性

描述

attributes

如果该节点是一个 Element ,则以 NamedNodeMap 形式返回该元素的属性。

childNodes

Node[] 的形式存放当前节点的子节点。如果没有子节点,则返回空数组。

firstChild

Node 的形式返回当前节点的第一个子节点。如果没有子节点,则为 null

lastChild

Node 的形式返回当前节点的最后一个子节点。如果没有子节点,则为 null

nextSibling

Node 的形式返回当前节点的兄弟下一个节点。如果没有这样的节点,则返回 null

nodeName

节点的名字, Element 节点则代表 Element 的标记名称。

nodeType

代表节点的类型。

parentNode

Node 的形式返回当前节点的父节点。如果没有父节点,则为 null

previousSibling

Node 的形式返回紧挨当前节点、位于它之前的兄弟节点。如果没有这样的节点,则返回 null

Node 对象常用方法:

方法

描述

appendChild()

通过把一个节点增加到当前节点的 childNodes[] 组,给文档树增加节点。

cloneNode()

复制当前节点,或者复制当前节点以及它的所有子孙节点。

hasChildNodes()

如果当前节点拥有子节点,则将返回 true

insertBefore()

给文档树插入一个节点,位置在当前节点的指定子节点之前。如果该节点已经存在,则删除之再插入到它的位置。

removeChild()

从文档树中删除并返回指定的子节点。

replaceChild()

从文档树中删除并返回指定的子节点,用另一个节点替换它。

接下来,让我们使用上述的 DOM 应用编程接口,来试着操作 HTML 文档。

A 、遍历文档的节点

DOM 把一个 HTML 文档视为树,因此,遍历整个树是应该是家常便饭。跟之前说过的一样,这里我们提供两个遍历树的例子。通过它,我们能够学会如何使用 childNodes[] firstChile lastChild nextSibling previousSibling 遍历整棵树。

例子 1-- sample3_1.htm

这个例子使用了 childNodes[] 和递归方式来遍历整个文档,统计文档中出现的 Element 元素总数,并把 Element 标记名全部打印出来。需要特别注意的是,在使用 DOM 时,必须等文档被装载完毕再执行遍历等行为操作文档。 sample3_1.htm 具体代码如下:

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title> 无标题文档 </title>

<script language="javascript">

var elementName = ""; // 全局变量,保存 Element 标记名,使用完毕要清空

function countTotalElement(node) { // 参数 node 是一个 Node 对象

       var total = 0;

       if(node.nodeType == 1) { // 检查 node 是否为 Element 对象

              total++;                 // 如果是,计数器加 1

              elementName = elementName + node.tagName + "\r\n"; // 保存标记名

       }

       var childrens = node.childNodes;         // 获取 node 的全部子节点

       for(var i=0;i<childrens.length;i++) {

              total += countTotalElement(childrens[i]); // 在每个子节点上进行递归操作

       }

       return total;

}

</script>

</head>

<body>

<a href="javascript:void(0)"

onClick="alert(' 标记总数: ' + countTotalElement(document) + '\r\n 全部标记如下: \r\n' + elementName);elementName='';"> 开始统计 </a>

</body>

</html>

运行效果如下:

遍历文档_1.jpg 遍历文档_2.jpg               

例子 2 – sample3_2.htm

接下来使用 firstChile lastChild nextSibling previousSibling 遍历整个文档树。修改一下 countTotalElement 函数,其他跟 sample3_1.htm 一样:

function countTotalElement(node) { // 参数 node 是一个 Node 对象

       var total = 0;

       if(node.nodeType == 1) { // 检查 node 是否为 Element 对象

              total++;                 // 如果是,计数器加 1

              elementName = elementName + node.tagName + "\r\n"; // 保存标记名

       }

       var childrens = node.childNodes;         // 获取 node 的全部子节点

       for(var m=node.firstChild; m!=null;m=m.nextSibling) {

              total += countTotalElement(m); // 在每个子节点上进行递归操作

       }

       return total;

}

B 、搜索文档中特定的元素

在使用 DOM 的过程中,有时候需要定位到文档中的某个特定节点,或者具有特定类型的节点列表。这种情况下,可以调用 Document 对象的 getElementsByTagName() getElementById() 方法来实现。

document.getElementsByTagName() 返回文档中具有指定标记名的全部 Element 节点数组(也是 NodeList 类型)。 Element 出现在数组中的顺序就是他们在文档中出现的顺序。传递给 getElementsByTagName() 的参数忽略大小写。比如,想定位到第一个 <table> 标记,可以这样写: document.getElementsByTagName(“table”)[0] 。例外的,可以使用 document.body 定位到 <body> 标记,因为它是唯一的。

getElementsByTagName() 返回的数组取决于文档。一旦文档改变,返回结果也立即改变。相比, getElementById() 则比较灵活,可以随时定位到目标,只是要实现给目标元素一个唯一的 id 属性值。这个我们在《 AJAX 开发简略》的“级联菜单”例子中已经使用过了。

Element 对象也支持 getElementsByTagName() getElementById() 。不同的是,搜索领域只针对调用者的子节点。

C 、修改文档内容

遍历整棵文档树、搜索特定的节点,我们最终目的之一是要修改文档内容。接下来的三个例子将使用 Node 的几个常用方法,来演示如何修改文档内容。

例子 3 -- sample4_1.htm

这个例子包含三个文本节点和一个按钮。点击按钮后,三个文本节点和按钮的顺序将被颠倒。程序使用了 Node appendChild() removeChild() 方法。

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title> 无标题文档 </title>

<script language="javascript">

       function reverseNode(node) { // 颠倒节点 node 的顺序

              var kids = node.childNodes; // 获取子节点列表

              var kidsNum = kids.length; // 统计子节点总数

              for(var i=kidsNum-1;i>=0;i--) { // 逆向遍历子节点列表

                     var c = node.removeChild(kids[i]); // 删除指定子节点,保存在 c

                     node.appendChild(c); // c 放在新位置上

              }

       }

</script>

</head>

<body>

<p> 第一行 </p>

<p> 第二行 </p>

<p> 第三行 </p>

<p><input type="button" name="reverseGo" value=" 颠倒 "

onClick="reverseNode(document.body)"></p>

</body>

</html>

修改文档_1.jpg 修改文档_2.jpg             

例子 4-- sample4_2.htm

例子 1 通过直接操作 body 的子节点来修改文档。在 HTML 文档中,布局和定位常常通过表格 <table> 来实现。因此,例子 4 将演示操作表格内容,将表格的四个单元行顺序颠倒。如果没有使用 <tbody> 标签,则 <table> 把全部的 <tr> 当做是属于一个子节点 <tbody> ,所以我们采用数组缓存的方式,把行数据颠倒一下。这个例子同时也演示了如何使用 DOM 创建表格单元行。

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title> 无标题文档 </title>

<script language="javascript">

function reverseTable() {

       var node = document.getElementsByTagName("table")[0]; // 第一个表格

       var child = node.getElementsByTagName("tr"); // 取得表格内的所有行

       var newChild = new Array(); // 定义缓存数组,保存行内容

       for(var i=0;i<child.length;i++) {

              newChild[i] = child[i].firstChild.innerHTML;

       }

       node.removeChild(node.childNodes[0]); // 删除全部单元行

       var header = node.createTHead(); // 新建表格行头

       for(var i=0;i<newChild.length;i++) {

              var headerrow = header.insertRow(i); // 插入一个单元行

              var cell = headerrow.insertCell(0); // 在单元行中插入一个单元格

              // 在单元格中创建 TextNode 节点

              cell.appendChild(document.createTextNode(newChild[newChild.length-i-1]));

       }

}

</script>

</head>

<body>

<table width="200" border="1" cellpadding="4" cellspacing="0">

    <tr>

        <td height="25"> 第一行 </td>

    </tr>

    <tr>

        <td height="25"> 第二行 </td>

    </tr>

    <tr>

        <td height="25"> 第三行 </td>

    </tr>

    <tr>

        <td height="25"> 第四行 </td>

    </tr>

</table>

<br>

<input type="button" name="reverse" value=" 开始颠倒 " onClick="reverseTable()">

</body>

</html>

     修改文档_3.jpg修改文档_4.jpg

例子 5 -- sample4_3.htm

正如我们在 Node 节点介绍部分所指出的那样, appendChild() replaceChild() removeChild() insertBefore() 方法会立即改变文档的结构。下面的例子包含两个表格,我们试着把表格二的内容替换表格一的内容。

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title> 无标题文档 </title>

<script language="javascript">

function replaceContent() {

       var table1 = document.getElementsByTagName("table")[0];

       var table2 = document.getElementsByTagName("table")[1];

       var kid1 = table1.firstChild.firstChild.firstChild; // 定位到 <td> 节点

       var kid2 = table2.firstChild.firstChild.firstChild; // 定位到 <td> 节点

       var repKid = kid2.firstChild; // 定位到表格二 <td> 内含的 TextNode 节点

       try {

              // 用表格二的单元格内容替换表格一的单元格内容,表格二变成没有单元格内容

              kid1.replaceChild(repKid,kid1.firstChild);

              // 下面注释如果开放,将出现 object error ,因为表格二已经被改变

              //kid2.replaceChild(kid1.firstChild,kid2.firstChild);

       }catch(e){

              alert(e);

       }

}

</script>

</head>

<body>

<table width="200" border="1" cellspacing="0" cellpadding="0">

<tbody>

    <tr>

        <td> 表格一 </td>

    </tr>

</tbody>

</table>

<br>

<table width="200" border="1" cellspacing="0" cellpadding="0">

<tbody>

    <tr>

        <td> 表格二 </td>

    </tr>

</tbody>

</table>

<br>

<input type="button" name="replaceNode" value=" 替换 " onClick="replaceContent()">

</body>

</html>

     修改文档_5.jpg修改文档_6.jpg

注意,当执行 kid1.replaceChild(repKid,kid1.firstChild); 的时候, table2 的子节点已经被转移到 table1 了, table2 已经没有子节点,不能再调用 table2 的子节点。看看代码的注释,试着运行一下,应该就知道文档是怎么改变的了。

D 、往文档添加新内容

在学会遍历、搜索、修改文档之后,我们现在试着网文档添加新的内容。其实没有什么新意,只是利用我们上述提到的 Node 的属性和方法而已,还是操作 <table> 标记的内容。有新意的是,我们要实现一个留言簿。是的,留言簿,你可以往里面留言,只是不能刷新噢。

例子 6 – sample5_1.htm

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title> 无标题文档 </title>

<script language="javascript">

function insertStr() {

       var f = document.form1;

       var value = f.str.value;

       if(value!="") {

              // 从最终的 TextNode 节点开始,慢慢形成 <tbody> 结构

              var text = document.createTextNode(value); // 新建一个 TextNode 节点

              var td = document.createElement("td"); // 新建一个 td 类型的 Element 节点

              var tr = document.createElement("tr"); // 新建一个 tr 类型的 Element 节点

              var tbody = document.createElement("tbody"); // 新建一个 tbody 类型的 Element 节点

              td.appendChild(text); // 将节点 text 加入 td

              tr.appendChild(td); // 将节点 td 加入 tr

              tbody.appendChild(tr); // 将节点 tr 加入 tbody

              var parNode = document.getElementById("table1"); // 定位到 table

              parNode.insertBefore(tbody,parNode.firstChild); // 将节点 tbody 插入到节点顶部

              //parNode.appendChild(tbody); // 将节点 tbody 加入节点尾部

       }

}

</script>

</head>

<body>

<form name="form1" method="post" action="">

    <input name="str" type="text" id="str" value="">

    <input name="insert" type="button" id="insert" value=" 留言 " onClick="insertStr()">

</form>

<table width="400" border="1" cellspacing="0" cellpadding="0" id="table1">

<tbody>

    <tr>

        <td height="25"> 网友留言列表: </td>

    </tr>

</tbody>

</table>

</body>

</html>

我们之前说过, <table> 的子节点是 <tbody> <tbody> 的子节点才是 <tr> <tr> <td> 的父节点,最后 <td> 内部的 TextNode 节点。所以,往 <table> 增加单元格行要逐级形成,就像往树里面添加一个枝桠一样,要有叶子有径。看看,这个留言簿是不是很简单啊。这个例子同时也演示了往 <table> 表格标记里面增加内容的另一种方法。

添加文档内容_1.jpg 添加文档内容_2.jpg
E
使用 DOM 操作 XML 文档

在数据表示方面, XML 文档更加结构化。 DOM 在支持 HTML 的基础上提供了一系列的 API ,支持针对 XML 的访问和操作。利用这些 API ,我们可以从 XML 中提取信息,动态的创建这些信息的 HTML 呈现文档。处理 XML 文档,通常遵循“加载 XML 文档 à 提取信息 à 加工信息 à 创建 HTML 文档”的过程。下面的例子演示了如何加载并处理 XML 文档。

这个例子包含两个 JS 函数。 loadXML() 负责加载 XML 文档,其中既包含加载 XML 文档的 2 DOM 代码,又有实现同样操作的 Microsoft 专有 API 代码。需要提醒注意的是,文档加载过程不是瞬间完成的,所以对 loadXML() 的调用将在加载文档完成之前返回。因此,需要传递给 loadXML() 一个引用,以便文档加载完成后调用。

例子中的另外一个函数 makeTable() ,则在 XML 文档加载完毕之后,使用最后前介绍过的 DOM 应用编程接口读取 XML 文档信息,并利用这些信息形成一个新的 table 表格。

例子 7 -- sample6_1.htm

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title> 无标题文档 </title>

<script language="javascript">

function loadXML(handler) {

       var url = "employees.xml";

       if(document.implementation&&document.implementation.createDocument) {

              var xmldoc = document.implementation.createDocument("", "", null);

              xmldoc.onload =  handler(xmldoc, url);

              xmldoc.load(url);

       }

       else if(window.ActiveXObject) {

              var xmldoc = new ActiveXObject("Microsoft.XMLDOM");

              xmldoc.onreadystatechange = function() {

                     if(xmldoc.readyState == 4) handler(xmldoc, url);

              }

              xmldoc.load(url);

       }

}

function makeTable(xmldoc, url) {

       var table = document.createElement("table");

       table.setAttribute("border","1");

       table.setAttribute("width","600");

       table.setAttribute("class","tab-content");

       document.body.appendChild(table);

       var caption = "Employee Data from " + url;

       table.createCaption().appendChild(document.createTextNode(caption));

       var header = table.createTHead();

       var headerrow = header.insertRow(0);

       headerrow.insertCell(0).appendChild(document.createTextNode(" 姓名 "));

       headerrow.insertCell(1).appendChild(document.createTextNode(" 职业 "));

       headerrow.insertCell(2).appendChild(document.createTextNode(" 工资 "));

       var employees = xmldoc.getElementsByTagName("employee");

       for(var i=0;i<employees.length;i++) {

              var e = employees[i];

              var name = e.getAttribute("name");

              var job = e.getElementsByTagName("job")[0].firstChild.data;

              var salary = e.getElementsByTagName("salary")[0].firstChild.data;

              var row = table.insertRow(i+1);

              row.insertCell(0).appendChild(document.createTextNode(name));

              row.insertCell(1).appendChild(document.createTextNode(job));

              row.insertCell(2).appendChild(document.createTextNode(salary));

       }

}

</script>

<link href="css/style.css" rel="stylesheet" type="text/css">

</head>

 

<body onLoad="loadXML(makeTable)">

</body>

</html>

供读取调用的 XML 文档 – employees.xml

<?xml version="1.0" encoding="gb2312"?>

<employees>

       <employee name="J.Doe">

              <job>Programmer</job>

              <salary>32768</salary>

       </employee>

       <employee name="A.Baker">

              <job>Sales</job>

              <salary>70000</salary>

       </employee>

       <employee name="Big Cheese">

              <job>CEO</job>

              <salary>100000</salary>

       </employee>

</employees>

操作XML文档.jpg

处理
XML 文档

脱离 XML 文档的 AJAX 是不完整的。在本部分未完成之前,有读者说 AJAX 改名叫 AJAH H 应该代表 HTML 吧)比较合适。应该承认, XML 文档在数据的结构化表示以及接口对接上有先天的优势,但也不是所有的数据都应该用 XML 表示。有些时候单纯的文本表示可能会更合适。下面先举个 AJAX 处理返回 XML 文档的例子再讨论什么时候使用 XML

7.5.1 、处理返回的 XML

例子 8 -- sample7_1.htm

在这个例子中,我们采用之前确定的 AJAX 开发框架,稍微修改一下 body 内容和 processRequest 的相应方式,将先前的 employees.xml 的内容读取出来并显示。

body 的内容如下:

<input type="button" name="read"

 value=" 读取 XML" onClick="send_request('employees.xml')">

processRequest() 方法修改如下:

       // 处理返回信息的函数

    function processRequest() {

        if (http_request.readyState == 4) { // 判断对象状态

            if (http_request.status == 200) { // 信息已经成功返回,开始处理信息

                            var returnObj = http_request.responseXML;

                            var xmlobj = http_request.responseXML;

                            var employees = xmlobj.getElementsByTagName("employee");

                            var feedbackStr = "";

                            for(var i=0;i<employees.length;i++) { // 循环读取 employees.xml 的内容

                                   var employee = employees[i];

                                   feedbackStr += " 员工: " + employee.getAttribute("name");

                                   feedbackStr +=

" 职位: " + employee.getElementsByTagName("job")[0].firstChild.data;

                                   feedbackStr +=

 " 工资: " + employee.getElementsByTagName("salary")[0].firstChild.data;

                                   feedbackStr +=  "\r\n";

                            }

                            alert(feedbackStr);

            } else { // 页面不正常

                alert(" 您所请求的页面有异常。 ");

            }

        }

}

运行一下,看来效果还不错:
处理返回的xml.jpg

7.5.2 、选择合适的 XML 生成方式

现在的 web 应用程序往往采用了 MVC 三层剥离的设计方式。 XML 作为一种数据保存、呈现、交互的文档,其数据往往是动态生成的,通常由 JavaBean 转换过来。由 JavaBean 转换成 XML 文档的方式有好几种,选择合适的转换方式往往能达到事半功倍的效果。下面介绍两种常用的方式,以便需要的时候根据情况取舍。

A 、类自行序列化成 XML

类自行序列化成 XML 即每个类都实现自己的 toXML() 方法,选择合适的 API 、适当的 XML 结构、尽量便捷的生成逻辑快速生成相应的 XML 文档。显然,这种方式必须要求每个类编写专门的 XML 生成代码,每个类只能调用自己的 toXML() 方法。应用诸如 JDOM 等一些现成的 API ,可以减少不少开发投入。例子 9 是一个利用 JDOM API 形成的 toXML() 方法。

例子 9 -- toXml() JDOM 实现 -- Employ 类的 toXml() 方法:

public Element toXml() { 

       Element employee = new Element(“employee”);

       Employee.setAttribute(“name”,name);

       Element jobE = new Element(“job”).addContent(job);

       employee.setContent(jobE);

       Element salaryE = new Element(“salary”).addContent(salary);

       employee.setContent(salaryE);

       return employee;

}

JDOM 提供了现成的 API ,使得序列化成 XML 的工作更加简单,我们只需要把 toXML() 外面包装一个 Document ,然后使用 XMLOutputter 把文档写入 servlet 就可以了。 toXml() 允许递归调用其子类的 toXML() 方法,以便生成包含子图的 XML 文档。

       使用类自行序列化成 XML 的方式,要每个类都实现自己的 toXML() 方法,而且存在数据模型与视图耦合的问题,即要么为每个可能的视图编写独立的 toXML() 方法,要么心甘情愿接收冗余的数据,一旦数据结构或者文档发生改变, toXML() 就要做必要的修改。

B 、页面模板生成 XML 方式

一般的,可以采用通用的页面模板技术来生成 XML 文档,这个 XML 文档可以符合任何需要的数据模型,供 AJAX 灵活的调用。另外,模板可以采用任何标记语言编写,提高工作效率。下面是一个采用 Struts 标签库编写的 XML 文档,输出之前提到的 employees.xml

Sample8_2.jsp

<%@ page contentType="application/xml; charset=gb2312" import="Employee"%>

<%@ page import="java.util.Collection,java.util.ArrayList"%>

<?xml version="1.0"?>

<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>

<%

Employee em1 = new Employee();

em1.setName("J.Doe");

em1.setJob("Programmer");

em1.setSalary("32768");

Employee em2 = new Employee();

em2.setName("A.Baker");

em2.setJob("Sales");

em2.setSalary("70000");

Employee em3 = new Employee();

em3.setName("Big Cheese");

em3.setJob("CEO");

em3.setSalary("100000");

Collection employees = new ArrayList();

employees.add(em1);

employees.add(em2);

employees.add(em3);

pageContext.setAttribute("employees",employees);

%>

<employees>

<logic:iterate name="employees" id="employee">

       <employee name="<bean:write name='employee' property='name'/>">

              <job><bean:write name="employee" property="job"/></job>

              <salary><bean:write name="employee" property="salary"/></salary>

       </employee>

</logic:iterate>

</employees>

页面模板生成XML.jpg
采用页面模板生成
XML 方式,需要为每个需要的的数据模型建立一个对立的 JSP 文件,用来生成符合规范的 XML 文档,而不能仅仅在类的 toXML() 方法中组织对象图来实现。不过,倒是可以更加方便的确保标记匹配、元素和属性的顺序正确以及 XML 实体正确转义。

参考资料中 Philip McCarthy 的文章还描述了一种 Javascript 对象标注的生成方式,本文在此不赘述。有兴趣的读者可以自行查看了解。

7.5.3 、如何在使用 XML 还是普通文本间权衡

使用 XML 文档确实有其方便之处。不过 XML 文档的某些问题倒是要考虑一下,比如说延迟,即服务器不能立即解析 XML 文档成为 DOM 模型。这个问题在一定程度上会影响 AJAX 要求的快速反应能力。另外,某些情况下我们并不需要使用 XML 来表示数据,比如说数据足够简单成只有一个字符串而已。就好像我们之前提到的数据校验和级联菜单的例子一样。所以,个人认为在下面这些情况下可以考虑使用 XML 来作为数据表示的介质:

l         数据比较复杂,需要用 XML 的结构化方式来表示

l         不用考虑带宽和处理效率支出

l         与系统其他 API 或者其他系统交互,作为一种数据中转中介

l         需要特定各式的输出视图而文本无法表示的

总之,要认真评估两种表示方式的表示成本和效率,选择合适的合理的表示方式。

在关于 AJAX 的系列文章的下一篇,我们将综合使用 DOM XML ,来实现一个可以持久化的简单留言簿。另外,还将试着模拟 MSN Space 的部分功能,来体会 AJAX 的魅力

posted on 2006-09-20 17:28 保尔任 阅读(163) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航:
 

<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(4)

随笔分类

随笔档案

文章分类

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜