明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

JSP與XML的結(jié)合

[摘要]綜述:可擴(kuò)展標(biāo)注語言(eXtensible Markup Language,XML)正被迅速的運(yùn)用于業(yè)界,它已作為與平臺(tái)、語言和協(xié)議無關(guān)的格式描述和交換數(shù)據(jù)的廣泛應(yīng)用標(biāo)準(zhǔn)。XML和它的輔助規(guī)范可用于描述數(shù)據(jù)的文檔表現(xiàn),描述XML文檔類型的限制,描述XML文檔和資源之間的鏈接,描述XML文檔的自動(dòng)轉(zhuǎn)...
綜述:可擴(kuò)展標(biāo)注語言(eXtensible Markup Language,XML)正被迅速的運(yùn)用于業(yè)界,它已作為與平臺(tái)、語言和協(xié)議無關(guān)的格式描述和交換數(shù)據(jù)的廣泛應(yīng)用標(biāo)準(zhǔn)。XML和它的輔助規(guī)范可用于描述數(shù)據(jù)的文檔表現(xiàn),描述XML文檔類型的限制,描述XML文檔和資源之間的鏈接,描述XML文檔的自動(dòng)轉(zhuǎn)換和格式化。

  如何開發(fā)自定義標(biāo)簽庫?

  我使用JavaScript/" target="_blank">JSP和ASP編程已經(jīng)有一段頗長的時(shí)間了,在兩種服務(wù)器端的編程方式中,我越來越覺得JavaScript/" target="_blank">JSP的功能要強(qiáng)大得多。不提別的,其中JavaScript/" target="_blank">JSP的標(biāo)簽庫就是我選擇JavaScript/" target="_blank">JSP作為首選服務(wù)器端Web應(yīng)用開發(fā)工具的原因。為什么?因?yàn)椋壕S護(hù)和開發(fā)的速度。在一個(gè)單一的服務(wù)器頁面中,你可以混合使用各種不同的腳本方法和對象。就?quot;混凝土"一樣,這種混合可令服務(wù)器端的腳本變得強(qiáng)大,并且讓服務(wù)器端的編程者設(shè)計(jì)出非常靈活和動(dòng)態(tài)的Web頁面。不過這種自由的混合也有其缺點(diǎn),那就是維護(hù)起來非常麻煩,特別是當(dāng)項(xiàng)目逐漸變大時(shí)。由于最終的產(chǎn)品是經(jīng)由一個(gè)傳統(tǒng)的Web設(shè)計(jì)者來維護(hù)的,因此會(huì)帶來問題。更糟糕的是,隨著代碼的復(fù)雜性增加,開發(fā)的速度就會(huì)變慢,不利于開發(fā)中等和大型的Web應(yīng)用,一旦開發(fā)完,站點(diǎn)還要找合格的編程者來維護(hù)這些頗為復(fù)雜的代碼。
幸好,JavaScript/" target="_blank">JSP提供了一個(gè)很好解決的辦法。標(biāo)簽庫提供了一個(gè)簡單的方法來建立一個(gè)可重用的代碼塊。一旦標(biāo)簽庫設(shè)計(jì)好,它就可以在許多項(xiàng)目中再次使用。更方便的是,與COM和J2EE不同,你無需學(xué)習(xí)任何其它的技巧就可以建立一個(gè)標(biāo)簽庫!只要你懂得寫JavaScript/" target="_blank">JSP,你就可以建立一個(gè)標(biāo)簽庫。標(biāo)簽庫還可以改善Web應(yīng)用的維護(hù)。這個(gè)是得益于JavaScript/" target="_blank">JSP頁面自定義標(biāo)簽的簡單XML接口。這樣,Web設(shè)計(jì)者甚至可以做到無需知道任何JavaScript/" target="_blank">JSP的知識(shí),就可以建立JavaScript/" target="_blank">JSP的Web應(yīng)用。這個(gè)開放式的Web開發(fā)對于團(tuán)隊(duì)運(yùn)作是非常有效的。JavaScript/" target="_blank">JSP編程者可以建立自定義的標(biāo)簽和后臺(tái)的代碼模塊,而Web設(shè)計(jì)者可以使用自定義的標(biāo)簽來建立Web應(yīng)用,并且將精力集中在Web設(shè)計(jì)上。

  1. 標(biāo)簽庫的定義
  JavaScript/" target="_blank">JSP標(biāo)簽庫(也稱自定義庫)可看成是一套產(chǎn)生基于XML腳本的方法,它經(jīng)由JavaBeans來支持。在概念上說,標(biāo)簽庫是非常簡單和可以重用的代碼構(gòu)造。
  執(zhí)行XML/XSL轉(zhuǎn)換的標(biāo)簽范例和HTML頁面

<%@ taglib uri="http://www.jspinsider.com/jspkit/JAXP" prefix="JAXP"%>
c:/xml/example.xml
c:/xml/example.xsl

  在這個(gè)例子中,通過使用簡單的標(biāo)簽來訪問后臺(tái)更為強(qiáng)大的代碼,一個(gè)XML被裝載,并且通過一個(gè)XSL文件來產(chǎn)生一個(gè)結(jié)果,并發(fā)送給客戶端,全部通過使用一個(gè)簡單的標(biāo)簽調(diào)用就做到了。
  自定義標(biāo)簽為在JavaScript/" target="_blank">JSP項(xiàng)目中創(chuàng)建易于重用的代碼打開了一扇大門。你所需要的只是標(biāo)簽庫和它的文檔說明。

  2. 標(biāo)簽的組件
  雖然標(biāo)簽庫非常易于使用,不過要建立一個(gè)內(nèi)里的設(shè)計(jì)來支持標(biāo)簽庫是頗復(fù)雜的,起碼要比建立一個(gè)簡單的JavaBean復(fù)雜。這個(gè)復(fù)雜是來自于標(biāo)簽庫是由幾部分構(gòu)成的。不過,你只需要知道Java和JavaScript/" target="_blank">JSP的知識(shí)就夠了。
一個(gè)簡單的標(biāo)簽由下面的元素構(gòu)成:
 、 JavaBeans:為了得到Java與生具來的面向?qū)ο蟮暮锰,可重用的代碼應(yīng)該放到一個(gè)獨(dú)立的代碼容器中。這些JavaBeans并不是標(biāo)簽庫的一部分。不過它是你的代碼庫用來執(zhí)行相關(guān)任務(wù)的基本代碼塊。
 、 標(biāo)簽處理:這是標(biāo)簽庫的真正核心。一個(gè)標(biāo)簽處理器將引用它需要的任何資源(你的JavaBeans)和訪問你的JavaScript/" target="_blank">JSP頁面的全部信息(pageContext對象)。JavaScript/" target="_blank">JSP頁面也會(huì)將所有已經(jīng)被設(shè)置的標(biāo)簽屬性和JavaScript/" target="_blank">JSP頁面上的標(biāo)簽體中的內(nèi)容傳送給標(biāo)簽處理器。在標(biāo)簽處理器處理完畢后,它將發(fā)回輸出到你的JavaScript/" target="_blank">JSP頁面進(jìn)行處理。
 、 標(biāo)簽庫的描述(tld文件):這是一個(gè)簡單的XML文件,它記錄著標(biāo)簽處理器的屬性、信息和位置。JavaScript/" target="_blank">JSP容器通過這個(gè)文件來得知從哪里及如何調(diào)用一個(gè)標(biāo)簽庫。
  ⑷ 網(wǎng)站的web.xml文件:這是你網(wǎng)站的初始化文件,在這個(gè)文件中,你定義了網(wǎng)站中用到的自定義標(biāo)簽,以及哪個(gè)tld文件用來描述每個(gè)自定義的標(biāo)簽。
  ⑸ 分發(fā)文件(一個(gè)WAR或者JAR文件):如果你想重用自定義標(biāo)簽的話,你需要一個(gè)方法來將它由一個(gè)項(xiàng)目轉(zhuǎn)移到另一個(gè)項(xiàng)目中。將標(biāo)簽庫打包為一個(gè)JAR文件是一個(gè)簡單而且有效的方式。
  ⑹ 在你的JavaScript/" target="_blank">JSP文件中作標(biāo)簽庫聲明:很簡單,如果要用到該標(biāo)簽的話,只要在頁面聲明一下就可以,其后,你就可以在該JavaScript/" target="_blank">JSP頁面的任何地方使用它。
  看來要做的工作很多,不過其實(shí)并不是很難。它的要點(diǎn)并不在于編碼,而是在于如何將各部分正確地組織起來。不過,這樣的分層是很重要的,它可令標(biāo)簽的使用靈活和更容易轉(zhuǎn)移。更重要的是,這些層的存在可讓處理建立標(biāo)簽的工程通過一個(gè)JavaScript/" target="_blank">JSP IDE(JavaScript/" target="_blank">JSP的集成開發(fā)環(huán)境)自動(dòng)完成。期望將來的JavaScript/" target="_blank">JSP IDE可自動(dòng)完成創(chuàng)建一個(gè)自定義標(biāo)簽的大部分工作,這樣你只需要寫代碼和標(biāo)簽處理就可以了。
  注意:一個(gè)標(biāo)簽處理僅定義一個(gè)自定義標(biāo)簽;一個(gè)標(biāo)簽庫是幾個(gè)處理相同任務(wù)的標(biāo)簽處理器的集合。

  3. 建立自己的標(biāo)簽
  以下將一步一步地教你如何建立自定義的標(biāo)簽,具體的例子是擴(kuò)展JavaScript/" target="_blank">JSP,令它擁有自己的HTML編碼功能。這個(gè)功能將所有的<和>字符用HTML代碼來代替。它可以很容易地?cái)U(kuò)展為做其它的編碼處理。為了簡化,這個(gè)例子只解釋了建立自定義標(biāo)簽的基本要素。
 、 創(chuàng)建一個(gè)JavaBean
  你代碼中任何可重新使用的部分都應(yīng)該放到一個(gè)JavaBean中。這個(gè)很重要,因?yàn)槟阋?jīng)常在項(xiàng)目的其它地方用到這些代碼。放在標(biāo)簽處理器中的任何代碼在標(biāo)簽外都是不可以重新使用的,因此將可重用的代碼部分獨(dú)立開來是很重要的。在這個(gè)例子總,為HTML編碼的邏輯是常用的,因此放到JavaBean中。
 、 HTML編碼JavaBean

/* HTML_Format.Java */
public class HTML_Format extends Object implements Java.io.Serializable {
/** 創(chuàng)建新的HTML_Format */
public HTML_Format() {}
/** 將一個(gè)字符串中所有的所有 < 和 > 字符用響應(yīng)的HTML編碼代替 */
public String HTML_Encode(String as_data)
{
int li_len = as_data.length();
/*string buffer的長度要比原來的字符串長*/
StringBuffer lsb_encode = new StringBuffer(li_len + (li_len/10));
/* 循環(huán)替換全部的< 和 > 字符 */
for( int li_count = 0 ; li_count < li_len ; li_count++)
{ String ls_next = String.valueOf(as_data.charAt(li_count));
if (ls_next.equals("<")) ls_next = "<";
if (ls_next.equals(">")) ls_next = ">";
lsb_encode.append( ls_next );
}
return( lsb_encode.toString() );
}
}

 、 創(chuàng)建一個(gè)標(biāo)簽處理器
  標(biāo)簽處理器使用以下的代碼:

HTML編碼標(biāo)簽處理器
import Java.io.IOException;
import Javax.servlet.jsp.*;
import Javax.servlet.jsp.tagext.*;
public class HTML_FormatTag extends BodyTagSupport
{
/* 1} 在標(biāo)簽?zāi)⿲?huì)調(diào)用這個(gè)函數(shù) */
public int doEndTag() throws JspTagException
{
try
{ /* 2}得到標(biāo)簽中的文本 */
BodyContent l_tagbody = getBodyContent();
String ls_output = "";
/* 3}如果標(biāo)簽體有文本,就處理它 */
if(l_tagbody != null)
{ HTML_Format l_format = new HTML_Format();
/* 3a} 將標(biāo)簽體的內(nèi)容轉(zhuǎn)換為一個(gè)字符串 */
String ls_html_text = l_tagbody.getString();
ls_output = l_format.HTML_Encode(ls_html_text);
}
/* 4}將結(jié)果寫回到數(shù)據(jù)流中 */
pageContext.getOut().write(ls_output.trim());
}
catch (IOException e)
{ throw new JspTagException("Tag Error:" + e.toString());
}
/* 讓JavaScript/" target="_blank">JSP繼續(xù)處理以下頁面的內(nèi)容 */
return EVAL_PAGE;
}
}

  這個(gè)處理很簡單,它包括有:
  o 讀入標(biāo)簽開始和結(jié)束間的文本
  o 調(diào)用html編碼函數(shù)
  o 返回結(jié)果到JavaScript/" target="_blank">JSP頁面。
 、 創(chuàng)建一個(gè)標(biāo)簽描述器
  需要描述自定義標(biāo)簽以讓系統(tǒng)知道如何處理。該描述文件的后綴為.tld,通常它的名字和標(biāo)簽處理器相同,并存放在"/WEB-INF/"目錄。

HTML編碼標(biāo)簽描述器
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JavaScript/" target="_blank">JSP Tag Library 1.1//EN"
"http://Java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<TAGLIB>
<TLIBVERSION>1.0</TLIBVERSION>
<JavaScript/" target="_blank">JSPVERSION>1.1</JavaScript/" target="_blank">JSPVERSION>
<SHORTNAME>HTML_FormatTag</SHORTNAME>
<URI></URI>
<INFO>HTML Encoding Tag </INFO>
<TAG>
<NAME>HTMLEncode</NAME>
<TAGCLASS>HTML_FormatTag</TAGCLASS>
<INFO>Encode HTML</INFO>
</TAG>
</TAGLIB>

[page_break] 、 更新Web XML文件
  現(xiàn)在可告訴JavaScript/" target="_blank">JSP容器使用標(biāo)簽庫。為此要修改web.xml文件,具體說來是要在其中加入一個(gè)taglib的項(xiàng)目來注冊該標(biāo)簽庫。最重要的是,要為tag分配一個(gè)URI。URI是一個(gè)唯一的引用,只應(yīng)用在該網(wǎng)站的這個(gè)特別的標(biāo)簽上。使用全長的URL或者包名是一個(gè)好的習(xí)慣,它可以確保唯一性,因?yàn)樵摌?biāo)簽可以在不同的網(wǎng)站使用。這個(gè)例子是簡化了。

修改web.xml文件
<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://Java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<WEB-APP>
<TAGLIB>
<TAGLIB-URI>
HTMLEncode
</TAGLIB-URI>
<TAGLIB-LOCATION>
/WEB-INF/HTML_FormatTag.tld
</TAGLIB-LOCATION>
</TAGLIB>
</WEB-APP>

 、 使用新的標(biāo)簽
  自定義的標(biāo)簽已經(jīng)設(shè)置好,可以用在一個(gè)JavaScript/" target="_blank">JSP頁面上。要做到這一點(diǎn),只需在該頁面使用taglib指示命令聲明一下該標(biāo)簽就可以了,該標(biāo)簽通過它唯一的URI被引用,并且會(huì)被分配一個(gè)名字空間前綴。前綴可以任意,只要它不與其它的名字空間沖突便可。
  在一個(gè)JavaScript/" target="_blank">JSP頁面上使用HTML編碼標(biāo)簽:

<%@ taglib uri="HTMLEncode" prefix="Examples" %>
<PRE>
<?XML:NAMESPACE PREFIX = Examples /><Examples:HTMLEncode>
< Hello , Simple sample >
</Examples:HTMLEncode>
</PRE>
范例代碼的輸出
< Hello , Simple sample >
which displays as:
< Hello , Simple sample >

  通過這個(gè)標(biāo)簽,我就將該頁面的所有代碼編碼了。有趣的是所有的自定義標(biāo)簽都是在服務(wù)器上處理的。這意味著你將不會(huì)在輸出的頁面上看到自定義的標(biāo)簽。
  建立一個(gè)標(biāo)簽不是很難吧。最困難的部分是要學(xué)習(xí)標(biāo)簽處理的所有細(xì)節(jié)。這是一個(gè)很強(qiáng)大的功能,我們只是提到了最基本的地方。由于這個(gè)處理需要幾步,新的JavaScript/" target="_blank">JSP編程者在創(chuàng)建標(biāo)簽時(shí)將會(huì)感到迷惑。

  如何利用JavaScript/" target="_blank">JSP開發(fā)DOM應(yīng)用?

  DOM是Document Object Model的縮寫,即文檔對象模型。XML將數(shù)據(jù)組織為一顆樹,所以DOM就是對這顆樹的一個(gè)對象描敘。通俗的說,就是通過解析XML文檔,為XML文檔在邏輯上建立一個(gè)樹模型,樹的節(jié)點(diǎn)是一個(gè)個(gè)對象。我們通過存取這些對象就能夠存取XML文檔的內(nèi)容。
  下面我們來看一個(gè)簡單的例子,看看在DOM中,我們是如何來操作一個(gè)XML文檔的。這是一個(gè)XML文檔,也是我們要操作的對象:

<?xml version="1.0" encoding="UTF-8"?>
<messages>
<message>Good-bye serialization, hello Java!</message>
</messages>

  下面,我們需要把這個(gè)文檔的內(nèi)容解析到一個(gè)個(gè)的Java對象中去供程序使用,利用JAXP,我們只需幾行代碼就能做到這一點(diǎn)。首先,我們需要建立一個(gè)解析器工廠,以利用這個(gè)工廠來獲得一個(gè)具體的解析器對象:
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  我們在這里使用DocumentBuilderFacotry的目的是為了創(chuàng)建與具體解析器無關(guān)的程序,當(dāng)DocumentBuilderFactory類的靜態(tài)方法newInstance()被調(diào)用時(shí),它根據(jù)一個(gè)系統(tǒng)變量來決定具體使用哪一個(gè)解析器。又因?yàn)樗械慕馕銎鞫挤䦶挠贘AXP所定義的接口,所以無論具體使用哪一個(gè)解析器,代碼都是一樣的。所以當(dāng)在不同的解析器之間進(jìn)行切換時(shí),只需要更改系統(tǒng)變量的值,而不用更改任何代碼。這就是工廠所帶來的好處。
  DocumentBuilder db = dbf.newDocumentBuilder();
  當(dāng)獲得一個(gè)工廠對象后,使用它的靜態(tài)方法newDocumentBuilder()方法可以獲得一個(gè)DocumentBuilder對象,這個(gè)對象代表了具體的DOM解析器。但具體是哪一種解析器,微軟的或者IBM的,對于程序而言并不重要。
  然后,我們就可以利用這個(gè)解析器來對XML文檔進(jìn)行解析了:
  Document doc = db.parse("c:/xml/message.xml");
  DocumentBuilder的parse()方法接受一個(gè)XML文檔名作為輸入?yún)?shù),返回一個(gè)Document對象,這個(gè)Document對象就代表了一個(gè)XML文檔的樹模型。以后所有的對XML文檔的操作,都與解析器無關(guān),直接在這個(gè)Document對象上進(jìn)行操作就可以了。而具體對Document操作的方法,就是由DOM所定義的了。
  從得到的Document對象開始,我們就可以開始我們的DOM之旅了。使用Document對象的getElementsByTagName()方法,我們可以得到一個(gè)NodeList對象,一個(gè)Node對象代表了一個(gè)XML文檔中的一個(gè)標(biāo)簽元素,而NodeList對象,觀其名而知其意,所代表的是一個(gè)Node對象的列表:
  NodeList nl = doc.getElementsByTagName("message");
  我們通過這樣一條語句所得到的是XML文檔中所有<message>標(biāo)簽對應(yīng)的Node對象的
  一個(gè)列表。然后,我們可以使用NodeList對象的item()方法來得到列表中的每一個(gè)Node對象:
  Node my_node = nl.item(0);
  當(dāng)一個(gè)Node對象被建立之后,保存在XML文檔中的數(shù)據(jù)就被提取出來并封裝在這個(gè)Node中了。在這個(gè)例子中,要提取Message標(biāo)簽內(nèi)的內(nèi)容,我們通常會(huì)使用Node對象的getNodeValue()方法:
String message = my_node.getFirstChild().getNodeValue();
  請注意,這里還使用了一個(gè)getFirstChild()方法來獲得message下面的第一個(gè)子Node對象。雖然在message標(biāo)簽下面除了文本外并沒有其它子標(biāo)簽或者屬性,但是我們堅(jiān)持在這里使用getFirseChild()方法,這主要和W3C對DOM的定義有關(guān)。W3C把標(biāo)簽內(nèi)的文本部分也定義成一個(gè)Node,所以先要得到代表文本的那個(gè)Node,我們才能夠使用getNodeValue()來獲取文本的內(nèi)容。現(xiàn)在,既然我們已經(jīng)能夠從XML文件中提取出數(shù)據(jù)了,我們就可以把這些數(shù)據(jù)用在合適的地方,來構(gòu)筑應(yīng)用程序。

  DOM實(shí)例

  先說說這個(gè)例子到底要做的是什么吧,我們在一個(gè)名為link.xml文件中保存了一些URL地址,我們希望可以通過DOM把這些URL讀出并顯示出來,也可以反過來向這個(gè)XML文件中寫入加入的URL地址。很簡單,卻很實(shí)用,也足夠來例示DOM的絕大部分用法了。
  第一個(gè)程序我們稱為xmldisplay.Java,主要的功能就是讀取這個(gè)XML文件中各個(gè)節(jié)點(diǎn)的內(nèi)容,然后在格式化輸出在System.out上,我們來看看這個(gè)程序:

import Javax.xml.parsers.*;
import org.w3c.dom.*;

  這是引入必要的類,因?yàn)樵谶@里使用的是Sun所提供的XML解析器,因而需要引入Java.xml.parsers包,其中包含了有DOM解析器和SAX解析器的具體實(shí)現(xiàn)。org.w3c.dom包中定義了w3c所制定的DOM接口。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document doc=builder.parse("links.xml");
doc.normalize();
 

  除了上面講到的,還有一個(gè)小技巧,對Document對象調(diào)用normalize(),可以去掉XML文檔中作為格式化內(nèi)容的空白而映射在DOM樹中的不必要的Text Node對象。否則你得到的DOM樹可能并不如你所想象的那樣。特別是在輸出的時(shí)候,這個(gè)normalize()更為有用。

  NodeList links =doc.getElementsByTagName("link");

  剛才說過,XML文檔中的空白符也會(huì)被作為對象映射在DOM樹中。因而,直接調(diào)用Node方法的getChildNodes方法有時(shí)候會(huì)有些問題,有時(shí)不能夠返回所期望的NodeList對象。解決的辦法是使用Element的getElementByTagName(String),返回的NodeLise就是所期待的對象了。然后,可以用item()方法提取想要的元素。

for (int i=0;i<links.getLength();i++){
Element link=(Element) links.item(i);
System.out.print("Content: ");
System.out.println(link.getElementsByTagName("text").item(0).getFirstChild();
.getNodeValue());
……

  上面的代碼片斷就完成了對XML文檔內(nèi)容的格式化輸出。只要注意到一些細(xì)節(jié)的問題,比如getFirstChile()方法和getElementsByTagName()方法的使用,這些還是比較容易的。
  下面的內(nèi)容,就是在修改了DOM樹后重新寫入到XML文檔中去的問題了。這個(gè)程序名為xmlwrite.Java。在JAXP1.0版本中,并沒有直接的類和方法能夠處理XML文檔的寫入問題,需要借助其它包中的一些輔助類。而在JAXP1.1版本中,引入了對XSLT的支持,所謂XSLT,就是對XML文檔進(jìn)行變換(Translation)后,得到一個(gè)新的文檔結(jié)構(gòu)。利用這個(gè)新加入的功能,我們就能夠很方便的把新生成或者修改后的DOM樹從新寫回到XML文件中去了,下面我們來看看代碼的實(shí)現(xiàn),這段代碼的主要功能是向links.xml文件中加入一個(gè)新的link節(jié)點(diǎn):

import Javax.xml.parsers.*;
import Javax.xml.transform.*;
import Javax.xml.transform.dom.DOMSource;
import Javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;

  新引入的Java.xml.transform包中的幾個(gè)類,就是用來處理XSLT變換的。
我們希望在上面的XML文件中加入一個(gè)新的link節(jié)點(diǎn),因而首先還是要讀入links.xml文件,構(gòu)建一個(gè)DOM樹,然后再對這個(gè)DOM樹進(jìn)行修改(添加節(jié)點(diǎn)),最后把修改后的DOM寫回到links.xml文件中:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder=factory.newDocumentBuilder();
Document doc=builder.parse("links.xml");
doc.normalize();
//---取得變量----
String text="Hanzhong's Homepage";
String url="www.hzliu.com";
String author="Hzliu Liu";
String discription="A site from Hanzhong Liu, give u lots of suprise!!!";

  為了看清重點(diǎn),簡化程序,我們把要加入的內(nèi)容硬編碼到記憶String對象中,而實(shí)際操作中,往往利用一個(gè)界面來提取用戶輸入,或者通過JDBC從數(shù)據(jù)庫中提取想要的內(nèi)容。

Text textseg;
Element link=doc.createElement("link");

  首先應(yīng)該明了的是,無論什么類型的Node,Text型的也好,Attr型的也好,Element型的也好,它們的創(chuàng)建都是通過Document對象中的createXXX()方法來創(chuàng)建的(XXX代表具體要?jiǎng)?chuàng)建的類型),因此,我們要向XML文檔中添加一個(gè)link項(xiàng)目,首先要?jiǎng)?chuàng)建一個(gè)link對象:

Element linktext=doc.createElement("text");
textseg=doc.createTextNode(text);
linktext.appendChild(textseg);
link.appendChild(linktext);
……

  創(chuàng)建節(jié)點(diǎn)的過程可能有些千篇一律,但需要注意的地方是,對Element中所包含的text(在DOM中,這些text也是代表了一個(gè)Node的,因此也必須為它們創(chuàng)建相應(yīng)的node),不能直接用Element對象的setNodeValue()方法來設(shè)置這些text的內(nèi)容,而需要用創(chuàng)建的Text對象的setNodeValue()方法來設(shè)置文本,這樣才能夠把創(chuàng)建的Element和其文本內(nèi)容添加到DOM樹中?纯辞懊娴拇a,你會(huì)更好的理解這一點(diǎn):
doc.getDocumentElement().appendChild(link);
  最后,不要忘記把創(chuàng)建好的節(jié)點(diǎn)添加到DOM樹中。Document類的getDocumentElement()方法,返回代表文檔根節(jié)點(diǎn)的Element對象。在XML文檔中,根節(jié)點(diǎn)一定是唯一的。

TransformerFactory tFactory =TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new Java.io.File("links.xml"));
transformer.transform(source, result);

  然后就是用XSLT把DOM樹輸出了。這里的TransformerFactory也同樣應(yīng)用了工廠模式,使得具體的代碼同具體的變換器無關(guān)。實(shí)現(xiàn)的方法和DocumentBuilderFactory相同,這兒就不贅述了。Transformer類的transfrom方法接受兩個(gè)參數(shù)、一個(gè)數(shù)據(jù)源Source和一個(gè)輸出目標(biāo)Result。這里分別使用的是DOMSource和StreamResult,這樣就能夠把DOM的內(nèi)容輸出到一個(gè)輸出流中,當(dāng)這個(gè)輸出流是一個(gè)文件的時(shí)候,DOM的內(nèi)容就被寫入到文件中去了。
[page_break]  如何利用JavaScript/" target="_blank">JSP開發(fā)SAX應(yīng)用?

  SAX是Simple API for XML的縮寫,它并不是由W3C官方所提出的標(biāo)準(zhǔn),可以說是"民間"的事實(shí)標(biāo)準(zhǔn)。實(shí)際上,它是一種社區(qū)性質(zhì)的討論產(chǎn)物。雖然如此,在XML中對SAX的應(yīng)用絲毫不比DOM少,幾乎所有的XML解析器都會(huì)支持它。
  與DOM比較而言,SAX是一種輕量型的方法。我們知道,在處理DOM的時(shí)候,我們需要讀入整個(gè)的XML文檔,然后在內(nèi)存中創(chuàng)建DOM樹,生成DOM樹上的每個(gè)Node對象。當(dāng)文檔比較小的時(shí)候,這不會(huì)造成什么問題,但是一旦文檔大起來,處理DOM就會(huì)變得相當(dāng)費(fèi)時(shí)費(fèi)力。特別是其對于內(nèi)存的需求,也將是成倍的增長,以至于在某些應(yīng)用中使用DOM是一件很不劃算的事(比如在applet中)。這時(shí)候,一個(gè)較好的替代解決方法就是SAX。
  SAX在概念上與DOM完全不同。首先,不同于DOM的文檔驅(qū)動(dòng),它是事件驅(qū)動(dòng)的,也就是說,它并不需要讀入整個(gè)文檔,而文檔的讀入過程也就是SAX的解析過程。所謂事件驅(qū)動(dòng),是指一種基于回調(diào)(callback)機(jī)制的程序運(yùn)行方法。(如果你對Java新的代理事件模型比較清楚的話,就會(huì)很容易理解這種機(jī)制了)在XMLReader接受XML文檔,在讀入XML文檔的過程中就進(jìn)行解析,也就是說讀入文檔的過程和解析的過程是同時(shí)進(jìn)行的,這和DOM區(qū)別很大。解析開始之前,需要向XMLReader注冊一個(gè)ContentHandler,也就是相當(dāng)于一個(gè)事件監(jiān)聽器,在ContentHandler中定義了很多方法,比如startDocument(),它定制了當(dāng)在解析過程中,遇到文檔開始時(shí)應(yīng)該處理的事情。當(dāng)XMLReader讀到合適的內(nèi)容,就會(huì)拋出相應(yīng)的事件,并把這個(gè)事件的處理權(quán)代理給ContentHandler,調(diào)用其相應(yīng)的方法進(jìn)行響應(yīng)。
  這樣泛泛的說來或許有些不容易理解,別急,后面的例子會(huì)讓你明白SAX的解析過程?纯催@個(gè)簡單XML文件:

<POEM>
<AUTHOR>Ogden Nash</AUTHOR>
<TITLE>Fleas</TITLE>
<LINE>Adam</LINE>
</POEM>

  當(dāng)XMLReader讀到<POEM>標(biāo)簽時(shí),就會(huì)調(diào)用ContentHandler.startElement()方法,并把標(biāo)簽名POEM作為參數(shù)傳遞過去。在你實(shí)現(xiàn)的startElement()方法中需要做相應(yīng)的動(dòng)作,以處理當(dāng)<POEM>出現(xiàn)時(shí)應(yīng)該做的事情。各個(gè)事件隨著解析的過程(也就是文檔讀入的過程)一個(gè)個(gè)順序的被拋出,相應(yīng)的方法也會(huì)被順序的調(diào)用,最后,當(dāng)解析完成,方法都被調(diào)用后,對文檔的處理也就完成了。下面的這個(gè)表,列出了在解析上面的那個(gè)XML文件的時(shí)候,順序被調(diào)用的方法:

  遇到的項(xiàng)目 方法回調(diào)

{文檔開始} startDocument()
<POEM> startElement(null,"POEM",null,{Attributes})
"\n" characters("<POEM>\n...", 6, 1)
<AUTHOR> startElement(null,"AUTHOR",null,{Attributes})
"Ogden Nash" characters("<POEM>\n...", 15, 10)
</AUTHOR> endElement(null,"AUTHOR",null)
"\n" characters("<POEM>\n...", 34, 1)
<TITLE> startElement(null,"TITLE",null,{Attributes})
"Fleas" characters("<POEM>\n...", 42, 5)
</TITLE> endElement(null,"TITLE",null)
"\n" characters("<POEM>\n...", 55, 1)
<LINE> startElement(null,"LINE",null,{Attributes})
"Adam" characters("<POEM>\n...", 62, 4)
</LINE> endElement(null,"LINE",null)
"\n" characters("<POEM>\n...", 67, 1)
</POEM> endElement(null,"POEM",null)
{文檔結(jié)束} endDocument()

  ContentHandler實(shí)際上是一個(gè)接口,當(dāng)處理特定的XML文件的時(shí)候,就需要為其創(chuàng)建一個(gè)實(shí)現(xiàn)了ContentHandler的類來處理特定的事件,可以說,這個(gè)實(shí)際上就是SAX處理XML文件的核心。下面我們來看看定義在其中的一些方法:
  void characters(char[] ch, int start, int length):這個(gè)方法用來處理在XML文件中讀到字符串,它的參數(shù)是一個(gè)字符數(shù)組,以及讀到的這個(gè)字符串在這個(gè)數(shù)組中的起始位置和長度,我們可以很容易的用String類的一個(gè)構(gòu)造方法來獲得這個(gè)字符串的String類:String charEncontered=new String(ch,start,length)。
  void startDocument():當(dāng)遇到文檔的開頭的時(shí)候,調(diào)用這個(gè)方法,可以在其中做一些預(yù)處理的工作。
  void endDocument():和上面的方法相對應(yīng),當(dāng)文檔結(jié)束的時(shí)候,調(diào)用這個(gè)方法,可以在其中做一些善后的工作。
  void startElement(String namespaceURI, String localName, String qName, Attributes atts):當(dāng)讀到一個(gè)開始標(biāo)簽的時(shí)候,會(huì)觸發(fā)這個(gè)方法。在SAX1.0版本中并不支持名域,而在新的2.0版本中提供了對名域的支持,這兒參數(shù)中的namespaceURI就是名域,localName是標(biāo)簽名,qName是標(biāo)簽的修飾前綴,當(dāng)沒有使用名域的時(shí)候,這兩個(gè)參數(shù)都未null。而atts是這個(gè)標(biāo)簽所包含的屬性列表。通過atts,可以得到所有的屬性名和相應(yīng)的值。要注意的是SAX中一個(gè)重要的特點(diǎn)就是它的流式處理,在遇到一個(gè)標(biāo)簽的時(shí)候,它并不會(huì)紀(jì)錄下以前所碰到的標(biāo)簽,也就是說,在startElement()方法中,所有你所知道的信息,就是標(biāo)簽的名字和屬性,至于標(biāo)簽的嵌套結(jié)構(gòu),上層標(biāo)簽的名字,是否有子元屬等等其它與結(jié)構(gòu)相關(guān)的信息,都是不得而知的,都需要你的程序來完成。這使得SAX在編程處理上沒有DOM來得那么方便。
  void endElement(String namespaceURI, String localName, String qName):這個(gè)方法和上面的方法相對應(yīng),在遇到結(jié)束標(biāo)簽的時(shí)候,調(diào)用這個(gè)方法。
  我們還是沿用講DOM的時(shí)候使用的那個(gè)文檔例子,但首先,我們先看一個(gè)簡單一些的應(yīng)用,我們希望能夠統(tǒng)計(jì)一下XML文件中各個(gè)標(biāo)簽出現(xiàn)的次數(shù)。這個(gè)例子很簡單,但是足以闡述SAX編程的基本思路了。一開始當(dāng)然還是import語句了:

import org.xml.sax.helpers.DefaultHandler;
import Javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import Java.util.*;
import Java.io.*;

  然后,我們創(chuàng)建一個(gè)繼承于DefaultHandler的類,具體的程序邏輯在這兒可以暫且放在一邊,要注意的是程序的結(jié)構(gòu):

public class SAXCounter extends DefaultHandler {
private Hashtable tags; //這個(gè)Hashtable用來記錄tag出現(xiàn)的次數(shù)
// 處理文檔前的工作
public void startDocument() throws SAXException {
tags = new Hashtable();//初始化Hashtable
}
//對每一個(gè)開始元屬進(jìn)行處理
public void startElement(String namespaceURI, String localName,
String rawName, Attributes atts)
throws SAXException
{
String key = localName;
……

  我們來看看這段程序作了些什么。在main()方法中,主要做的就是創(chuàng)建解析器,然后解析文檔。實(shí)際上,在這兒創(chuàng)建SAXParser對象的時(shí)候,為了使程序代碼于具體的解析器無關(guān),使用了同DOM中一樣的設(shè)計(jì)技巧:通過一個(gè)SAXParserFactory類來創(chuàng)建具體的SAXParser對象,這樣,當(dāng)需要使用不同的解析器的時(shí)候,要改變的,只是一個(gè)環(huán)境變量的值,而程序的代碼可以保持不變。這就是FactoryMethod模式的思想。在這兒不再具體講了,如果還有不明白的,可以參看上面DOM中的解釋,原理是一樣的。
  不過在這兒還有一點(diǎn)點(diǎn)要注意的地方,就是SAXParser類和XMLReader類之間的關(guān)系。你可能有些迷糊了吧,實(shí)際上SAXParser是JAXP中對XMLReader的一個(gè)封裝類,而XMLReader是定義在SAX2.0種的一個(gè)用來解析文檔的接口。你可以同樣的調(diào)用SAXParser或者XMLReader中的parser()方法來解析文檔,效果是完全一樣的。不過在SAXParser中的parser()方法接受更多的參數(shù),可以對不同的XML文檔數(shù)據(jù)源進(jìn)行解析,因而使用起來要比XMLReader要方便一些。
  這個(gè)例子僅僅涉及了SAX的一點(diǎn)皮毛,而下面的這個(gè),可就要高級一些了。下面我們要實(shí)現(xiàn)的功能,在DOM的例子中已經(jīng)有實(shí)現(xiàn)了,就是從XML文檔中讀出內(nèi)容并格式化輸出,雖然程序邏輯看起來還是很簡單,但是SAX可不比DOM哦,看著吧。
  前面說過,當(dāng)遇到一個(gè)開始標(biāo)簽的時(shí)候,在startElement()方法中,我們并不能夠得到這個(gè)標(biāo)簽在XML文檔中所處的位置。這在處理XML文檔的時(shí)候是個(gè)大麻煩,因?yàn)樵赬ML中標(biāo)簽的語義,有一部分是由其所處的位置所決定的。而且在一些需要驗(yàn)證文檔結(jié)構(gòu)的程序中,這更是一個(gè)問題。當(dāng)然,沒有解決不了的問題了,我們可以使用一個(gè)棧來實(shí)現(xiàn)對文檔結(jié)構(gòu)的紀(jì)錄。
  棧的特點(diǎn)是先進(jìn)先出,我們現(xiàn)在的想法是,在startElemnt()方法中用push將這個(gè)標(biāo)簽的名字添加到棧中,在endElement()方法中在把它pop出來。我們知道對一個(gè)結(jié)構(gòu)良好的XML而言,其嵌套結(jié)構(gòu)是完備的,每一個(gè)開始標(biāo)簽總會(huì)對應(yīng)一個(gè)結(jié)束標(biāo)簽,而且不會(huì)出現(xiàn)標(biāo)簽嵌套之間的錯(cuò)位。因而,每一次startElement()方法的調(diào)用,必然會(huì)對應(yīng)一個(gè)endElement()方法的調(diào)用,這樣push和pop也是成對出現(xiàn)的,我們只需要分析棧的結(jié)構(gòu),就可以很容易的知道當(dāng)前標(biāo)簽所處在文檔結(jié)構(gòu)中的位置了。

public class SAXReader extends DefaultHandler {
Java.util.Stack tags=new Java.util.Stack();
……

  在這兒雖然沒有使用到棧的分析,但實(shí)際上棧的分析是一件很容易的事情,應(yīng)為Java.util.Stack繼承了Java.util.Vector類,而且Stack中的元素是按棧的結(jié)構(gòu)由底至上排列的,因個(gè),我們可以使用Vector類的size()方法來得到Stack的元素個(gè)數(shù),還可以使用Vector的get(int)方法來得到具體的每一個(gè)元屬。實(shí)際上,如果把Stack的元素從底向上逐一排列出來,我們就得到了從XML根節(jié)點(diǎn)到當(dāng)前節(jié)點(diǎn)的一條唯一的路徑,有了這條路徑的信息,文檔的結(jié)構(gòu)就在清楚不過了。
  到目前為止,我們已經(jīng)掌握了對于XML編程的兩大利器:DOM和SAX,也知道了該如何在一個(gè)Java程序中使用它們。DOM編程相對簡單,但是速度比較慢,占用內(nèi)存多,而S AX編程復(fù)雜一些,但是速度快,占用內(nèi)存少。所以,我們應(yīng)該根據(jù)不同的環(huán)境選擇使用不同的方法。大部分的XML應(yīng)用基本都可以用它們來解決。需要特別說明的是,DOM和SAX其實(shí)都是語言無關(guān)的,并非Java所獨(dú)有,也就是說,只要有相應(yīng)的語言實(shí)現(xiàn),DOM和SAX可以應(yīng)用在任何面向?qū)ο蟮恼Z言中。



標(biāo)簽:JSP與XML的結(jié)合 

相關(guān)文章