綜述:可擴(kuò)展標(biāo)注語(yǔ)言(eXtensible Markup Language,XML)正被迅速的運(yùn)用于業(yè)界,它已作為與平臺(tái)、語(yǔ)言和協(xié)議無(wú)關(guān)的格式描述和交換數(shù)據(jù)的廣泛應(yīng)用標(biāo)準(zhǔn)。XML和它的輔助規(guī)范可用于描述數(shù)據(jù)的文檔表現(xiàn),描述XML文檔類型的限制,描述XML文檔和資源之間的鏈接,描述XML文檔的自動(dòng)轉(zhuǎn)換和格式化。 如何開(kāi)發(fā)自定義標(biāo)簽庫(kù)?
我使用JSP和ASP編程已經(jīng)有一段頗長(zhǎng)的時(shí)間了,在兩種服務(wù)器端的編程方式中,我越來(lái)越覺(jué)得JSP的功能要強(qiáng)大得多。不提別的,其中JSP的標(biāo)簽庫(kù)就是我選擇JSP作為首選服務(wù)器端Web應(yīng)用開(kāi)發(fā)工具的原因。為什么?因?yàn)椋壕S護(hù)和開(kāi)發(fā)的速度。在一個(gè)單一的服務(wù)器頁(yè)面中,你可以混合使用各種不同的腳本方法和對(duì)象。就?quot;混凝土"一樣,這種混合可令服務(wù)器端的腳本變得強(qiáng)大,并且讓服務(wù)器端的編程者設(shè)計(jì)出非常靈活和動(dòng)態(tài)的Web頁(yè)面。不過(guò)這種自由的混合也有其缺點(diǎn),那就是維護(hù)起來(lái)非常麻煩,特別是當(dāng)項(xiàng)目逐漸變大時(shí)。由于最終的產(chǎn)品是經(jīng)由一個(gè)傳統(tǒng)的Web設(shè)計(jì)者來(lái)維護(hù)的,因此會(huì)帶來(lái)問(wèn)題。更糟糕的是,隨著代碼的復(fù)雜性增加,開(kāi)發(fā)的速度就會(huì)變慢,不利于開(kāi)發(fā)中等和大型的Web應(yīng)用,一旦開(kāi)發(fā)完,站點(diǎn)還要找合格的編程者來(lái)維護(hù)這些頗為復(fù)雜的代碼。
幸好,JSP提供了一個(gè)很好解決的辦法。標(biāo)簽庫(kù)提供了一個(gè)簡(jiǎn)單的方法來(lái)建立一個(gè)可重用的代碼塊。一旦標(biāo)簽庫(kù)設(shè)計(jì)好,它就可以在許多項(xiàng)目中再次使用。更方便的是,與COM和J2EE不同,你無(wú)需學(xué)習(xí)任何其它的技巧就可以建立一個(gè)標(biāo)簽庫(kù)!只要你懂得寫(xiě)JSP,你就可以建立一個(gè)標(biāo)簽庫(kù)。標(biāo)簽庫(kù)還可以改善Web應(yīng)用的維護(hù)。這個(gè)是得益于JSP頁(yè)面自定義標(biāo)簽的簡(jiǎn)單XML接口。這樣,Web設(shè)計(jì)者甚至可以做到無(wú)需知道任何JSP的知識(shí),就可以建立JSP的Web應(yīng)用。這個(gè)開(kāi)放式的Web開(kāi)發(fā)對(duì)于團(tuán)隊(duì)運(yùn)作是非常有效的。JSP編程者可以建立自定義的標(biāo)簽和后臺(tái)的代碼模塊,而Web設(shè)計(jì)者可以使用自定義的標(biāo)簽來(lái)建立Web應(yīng)用,并且將精力集中在Web設(shè)計(jì)上。
1. 標(biāo)簽庫(kù)的定義
JSP標(biāo)簽庫(kù)(也稱自定義庫(kù))可看成是一套產(chǎn)生基于XML腳本的方法,它經(jīng)由JavaBeans來(lái)支持。在概念上說(shuō),標(biāo)簽庫(kù)是非常簡(jiǎn)單和可以重用的代碼構(gòu)造。
執(zhí)行XML/XSL轉(zhuǎn)換的標(biāo)簽范例和HTML頁(yè)面
<%@ taglib uri=" http://www.jspinsider.com/jspkit/JAXP " prefix="JAXP"%>
c:/xml/example.xml
c:/xml/example.xsl
在這個(gè)例子中,通過(guò)使用簡(jiǎn)單的標(biāo)簽來(lái)訪問(wèn)后臺(tái)更為強(qiáng)大的代碼,一個(gè)XML被裝載,并且通過(guò)一個(gè)XSL文件來(lái)產(chǎn)生一個(gè)結(jié)果,并發(fā)送給客戶端,全部通過(guò)使用一個(gè)簡(jiǎn)單的標(biāo)簽調(diào)用就做到了。
自定義標(biāo)簽為在JSP項(xiàng)目中創(chuàng)建易于重用的代碼打開(kāi)了一扇大門(mén)。你所需要的只是標(biāo)簽庫(kù)和它的文檔說(shuō)明。
2. 標(biāo)簽的組件
雖然標(biāo)簽庫(kù)非常易于使用,不過(guò)要建立一個(gè)內(nèi)里的設(shè)計(jì)來(lái)支持標(biāo)簽庫(kù)是頗復(fù)雜的,起碼要比建立一個(gè)簡(jiǎn)單的JavaBean復(fù)雜。這個(gè)復(fù)雜是來(lái)自于標(biāo)簽庫(kù)是由幾部分構(gòu)成的。不過(guò),你只需要知道Java和JSP的知識(shí)就夠了。
一個(gè)簡(jiǎn)單的標(biāo)簽由下面的元素構(gòu)成:
⑴ JavaBeans:為了得到Java與生具來(lái)的面向?qū)ο蟮暮锰帲芍赜玫拇a應(yīng)該放到一個(gè)獨(dú)立的代碼容器中。這些JavaBeans并不是標(biāo)簽庫(kù)的一部分。不過(guò)它是你的代碼庫(kù)用來(lái)執(zhí)行相關(guān)任務(wù)的基本代碼塊。
⑵ 標(biāo)簽處理:這是標(biāo)簽庫(kù)的真正核心。一個(gè)標(biāo)簽處理器將引用它需要的任何資源(你的JavaBeans)和訪問(wèn)你的JSP頁(yè)面的全部信息(pageContext對(duì)象)。JSP頁(yè)面也會(huì)將所有已經(jīng)被設(shè)置的標(biāo)簽屬性和JSP頁(yè)面上的標(biāo)簽體中的內(nèi)容傳送給標(biāo)簽處理器。在標(biāo)簽處理器處理完畢后,它將發(fā)回輸出到你的JSP頁(yè)面進(jìn)行處理。
⑶ 標(biāo)簽庫(kù)的描述(tld文件):這是一個(gè)簡(jiǎn)單的XML文件,它記錄著標(biāo)簽處理器的屬性、信息和位置。JSP容器通過(guò)這個(gè)文件來(lái)得知從哪里及如何調(diào)用一個(gè)標(biāo)簽庫(kù)。
⑷ 網(wǎng)站的web.xml文件:這是你網(wǎng)站的初始化文件,在這個(gè)文件中,你定義了網(wǎng)站中用到的自定義標(biāo)簽,以及哪個(gè)tld文件用來(lái)描述每個(gè)自定義的標(biāo)簽。
⑸ 分發(fā)文件(一個(gè)WAR或者JAR文件):如果你想重用自定義標(biāo)簽的話,你需要一個(gè)方法來(lái)將它由一個(gè)項(xiàng)目轉(zhuǎn)移到另一個(gè)項(xiàng)目中。將標(biāo)簽庫(kù)打包為一個(gè)JAR文件是一個(gè)簡(jiǎn)單而且有效的方式。
⑹ 在你的JSP文件中作標(biāo)簽庫(kù)聲明:很簡(jiǎn)單,如果要用到該標(biāo)簽的話,只要在頁(yè)面聲明一下就可以,其后,你就可以在該JSP頁(yè)面的任何地方使用它。
看來(lái)要做的工作很多,不過(guò)其實(shí)并不是很難。它的要點(diǎn)并不在于編碼,而是在于如何將各部分正確地組織起來(lái)。不過(guò),這樣的分層是很重要的,它可令標(biāo)簽的使用靈活和更容易轉(zhuǎn)移。更重要的是,這些層的存在可讓處理建立標(biāo)簽的工程通過(guò)一個(gè)JSP IDE(JSP的集成開(kāi)發(fā)環(huán)境)自動(dòng)完成。期望將來(lái)的JSP IDE可自動(dòng)完成創(chuàng)建一個(gè)自定義標(biāo)簽的大部分工作,這樣你只需要寫(xiě)代碼和標(biāo)簽處理就可以了。
注意:一個(gè)標(biāo)簽處理僅定義一個(gè)自定義標(biāo)簽;一個(gè)標(biāo)簽庫(kù)是幾個(gè)處理相同任務(wù)的標(biāo)簽處理器的集合。
3. 建立自己的標(biāo)簽
以下將一步一步地教你如何建立自定義的標(biāo)簽,具體的例子是擴(kuò)展JSP,令它擁有自己的HTML編碼功能。這個(gè)功能將所有的<和>字符用HTML代碼來(lái)代替。它可以很容易地?cái)U(kuò)展為做其它的編碼處理。為了簡(jiǎn)化,這個(gè)例子只解釋了建立自定義標(biāo)簽的基本要素。
⑴ 創(chuàng)建一個(gè)JavaBean
你代碼中任何可重新使用的部分都應(yīng)該放到一個(gè)JavaBean中。這個(gè)很重要,因?yàn)槟阋?jīng)常在項(xiàng)目的其它地方用到這些代碼。放在標(biāo)簽處理器中的任何代碼在標(biāo)簽外都是不可以重新使用的,因此將可重用的代碼部分獨(dú)立開(kāi)來(lái)是很重要的。在這個(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的長(zhǎng)度要比原來(lái)的字符串長(zhǎng)*/
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é)果寫(xiě)回到數(shù)據(jù)流中 */
pageContext.getOut().write(ls_output.trim());
}
catch (IOException e)
{ throw new JspTagException("Tag Error:" + e.toString());
}
/* 讓JSP繼續(xù)處理以下頁(yè)面的內(nèi)容 */
return EVAL_PAGE;
}
}
這個(gè)處理很簡(jiǎn)單,它包括有:
o 讀入標(biāo)簽開(kāi)始和結(jié)束間的文本
o 調(diào)用html編碼函數(shù)
o 返回結(jié)果到JSP頁(yè)面。
⑷ 創(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 JSP Tag Library 1.1//EN"
" http://Java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd ">
<TAGLIB>
<TLIBVERSION>1.0</TLIBVERSION>
<JSPVERSION>1.1</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>
⑸ 更新Web XML文件
現(xiàn)在可告訴JSP容器使用標(biāo)簽庫(kù)。為此要修改web.xml文件,具體說(shuō)來(lái)是要在其中加入一個(gè)taglib的項(xiàng)目來(lái)注冊(cè)該標(biāo)簽庫(kù)。最重要的是,要為tag分配一個(gè)URI。URI是一個(gè)唯一的引用,只應(yīng)用在該網(wǎng)站的這個(gè)特別的標(biāo)簽上。使用全長(zhǎng)的URL或者包名是一個(gè)好的習(xí)慣,它可以確保唯一性,因?yàn)樵摌?biāo)簽可以在不同的網(wǎng)站使用。這個(gè)例子是簡(jiǎn)化了。
修改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è)JSP頁(yè)面上。要做到這一點(diǎn),只需在該頁(yè)面使用taglib指示命令聲明一下該標(biāo)簽就可以了,該標(biāo)簽通過(guò)它唯一的URI被引用,并且會(huì)被分配一個(gè)名字空間前綴。前綴可以任意,只要它不與其它的名字空間沖突便可。
在一個(gè)JSP頁(yè)面上使用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 >
通過(guò)這個(gè)標(biāo)簽,我就將該頁(yè)面的所有代碼編碼了。有趣的是所有的自定義標(biāo)簽都是在服務(wù)器上處理的。這意味著你將不會(huì)在輸出的頁(yè)面上看到自定義的標(biāo)簽。
建立一個(gè)標(biāo)簽不是很難吧。最困難的部分是要學(xué)習(xí)標(biāo)簽處理的所有細(xì)節(jié)。這是一個(gè)很強(qiáng)大的功能,我們只是提到了最基本的地方。由于這個(gè)處理需要幾步,新的JSP編程者在創(chuàng)建標(biāo)簽時(shí)將會(huì)感到迷惑。
如何利用JSP開(kāi)發(fā)DOM應(yīng)用?
DOM是Document Object Model的縮寫(xiě),即文檔對(duì)象模型。XML將數(shù)據(jù)組織為一顆樹(shù),所以DOM就是對(duì)這顆樹(shù)的一個(gè)對(duì)象描敘。通俗的說(shuō),就是通過(guò)解析XML文檔,為XML文檔在邏輯上建立一個(gè)樹(shù)模型,樹(shù)的節(jié)點(diǎn)是一個(gè)個(gè)對(duì)象。我們通過(guò)存取這些對(duì)象就能夠存取XML文檔的內(nèi)容。
下面我們來(lái)看一個(gè)簡(jiǎn)單的例子,看看在DOM中,我們是如何來(lái)操作一個(gè)XML文檔的。這是一個(gè)XML文檔,也是我們要操作的對(duì)象:
<?xml version="1.0" encoding="UTF-8"?>
<messages>
<message>Good-bye serialization, hello Java!</message>
</messages>
下面,我們需要把這個(gè)文檔的內(nèi)容解析到一個(gè)個(gè)的Java對(duì)象中去供程序使用,利用JAXP,我們只需幾行代碼就能做到這一點(diǎn)。首先,我們需要建立一個(gè)解析器工廠,以利用這個(gè)工廠來(lái)獲得一個(gè)具體的解析器對(duì)象:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
我們?cè)谶@里使用DocumentBuilderFacotry的目的是為了創(chuàng)建與具體解析器無(wú)關(guān)的程序,當(dāng)DocumentBuilderFactory類的靜態(tài)方法newInstance()被調(diào)用時(shí),它根據(jù)一個(gè)系統(tǒng)變量來(lái)決定具體使用哪一個(gè)解析器。又因?yàn)樗械慕馕銎鞫挤挠?span lang="EN-US">JAXP所定義的接口,所以無(wú)論具體使用哪一個(gè)解析器,代碼都是一樣的。所以當(dāng)在不同的解析器之間進(jìn)行切換時(shí),只需要更改系統(tǒng)變量的值,而不用更改任何代碼。這就是工廠所帶來(lái)的好處。
DocumentBuilder db = dbf.newDocumentBuilder();
當(dāng)獲得一個(gè)工廠對(duì)象后,使用它的靜態(tài)方法newDocumentBuilder()方法可以獲得一個(gè)DocumentBuilder對(duì)象,這個(gè)對(duì)象代表了具體的DOM解析器。但具體是哪一種解析器,微軟的或者IBM的,對(duì)于程序而言并不重要。
然后,我們就可以利用這個(gè)解析器來(lái)對(duì)XML文檔進(jìn)行解析了:
Document doc = db.parse("c:/xml/message.xml");
DocumentBuilder的parse()方法接受一個(gè)XML文檔名作為輸入?yún)?shù),返回一個(gè)Document對(duì)象,這個(gè)Document對(duì)象就代表了一個(gè)XML文檔的樹(shù)模型。以后所有的對(duì)XML文檔的操作,都與解析器無(wú)關(guān),直接在這個(gè)Document對(duì)象上進(jìn)行操作就可以了。而具體對(duì)Document操作的方法,就是由DOM所定義的了。
從得到的Document對(duì)象開(kāi)始,我們就可以開(kāi)始我們的DOM之旅了。使用Document對(duì)象的getElementsByTagName()方法,我們可以得到一個(gè)NodeList對(duì)象,一個(gè)Node對(duì)象代表了一個(gè)XML文檔中的一個(gè)標(biāo)簽元素,而NodeList對(duì)象,觀其名而知其意,所代表的是一個(gè)Node對(duì)象的列表:
NodeList nl = doc.getElementsByTagName("message");
我們通過(guò)這樣一條語(yǔ)句所得到的是XML文檔中所有<message>標(biāo)簽對(duì)應(yīng)的Node對(duì)象的
一個(gè)列表。然后,我們可以使用NodeList對(duì)象的item()方法來(lái)得到列表中的每一個(gè)Node對(duì)象:
Node my_node = nl.item(0);
當(dāng)一個(gè)Node對(duì)象被建立之后,保存在XML文檔中的數(shù)據(jù)就被提取出來(lái)并封裝在這個(gè)Node中了。在這個(gè)例子中,要提取Message標(biāo)簽內(nèi)的內(nèi)容,我們通常會(huì)使用Node對(duì)象的getNodeValue()方法:
String message = my_node.getFirstChild().getNodeValue();
請(qǐng)注意,這里還使用了一個(gè)getFirstChild()方法來(lái)獲得message下面的第一個(gè)子Node對(duì)象。雖然在message標(biāo)簽下面除了文本外并沒(méi)有其它子標(biāo)簽或者屬性,但是我們堅(jiān)持在這里使用getFirseChild()方法,這主要和W3C對(duì)DOM的定義有關(guān)。W3C把標(biāo)簽內(nèi)的文本部分也定義成一個(gè)Node,所以先要得到代表文本的那個(gè)Node,我們才能夠使用getNodeValue()來(lái)獲取文本的內(nèi)容。現(xiàn)在,既然我們已經(jīng)能夠從XML文件中提取出數(shù)據(jù)了,我們就可以把這些數(shù)據(jù)用在合適的地方,來(lái)構(gòu)筑應(yīng)用程序。
DOM實(shí)例
先說(shuō)說(shuō)這個(gè)例子到底要做的是什么吧,我們?cè)谝粋€(gè)名為link.xml文件中保存了一些URL地址,我們希望可以通過(guò)DOM把這些URL讀出并顯示出來(lái),也可以反過(guò)來(lái)向這個(gè)XML文件中寫(xiě)入加入的URL地址。很簡(jiǎn)單,卻很實(shí)用,也足夠來(lái)例示DOM的絕大部分用法了。
第一個(gè)程序我們稱為xmldisplay.Java,主要的功能就是讀取這個(gè)XML文件中各個(gè)節(jié)點(diǎn)的內(nèi)容,然后在格式化輸出在System.out上,我們來(lái)看看這個(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è)小技巧,對(duì)Document對(duì)象調(diào)用normalize(),可以去掉XML文檔中作為格式化內(nèi)容的空白而映射在DOM樹(shù)中的不必要的Text Node對(duì)象。否則你得到的DOM樹(shù)可能并不如你所想象的那樣。特別是在輸出的時(shí)候,這個(gè)normalize()更為有用。
NodeList links =doc.getElementsByTagName("link");
剛才說(shuō)過(guò),XML文檔中的空白符也會(huì)被作為對(duì)象映射在DOM樹(shù)中。因而,直接調(diào)用Node方法的getChildNodes方法有時(shí)候會(huì)有些問(wèn)題,有時(shí)不能夠返回所期望的NodeList對(duì)象。解決的辦法是使用Element的getElementByTagName(String),返回的NodeLise就是所期待的對(duì)象了。然后,可以用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());
……
上面的代碼片斷就完成了對(duì)XML文檔內(nèi)容的格式化輸出。只要注意到一些細(xì)節(jié)的問(wèn)題,比如getFirstChile()方法和getElementsByTagName()方法的使用,這些還是比較容易的。
下面的內(nèi)容,就是在修改了DOM樹(shù)后重新寫(xiě)入到XML文檔中去的問(wèn)題了。這個(gè)程序名為xmlwrite.Java。在JAXP1.0版本中,并沒(méi)有直接的類和方法能夠處理XML文檔的寫(xiě)入問(wèn)題,需要借助其它包中的一些輔助類。而在JAXP1.1版本中,引入了對(duì)XSLT的支持,所謂XSLT,就是對(duì)XML文檔進(jìn)行變換(Translation)后,得到一個(gè)新的文檔結(jié)構(gòu)。利用這個(gè)新加入的功能,我們就能夠很方便的把新生成或者修改后的DOM樹(shù)從新寫(xiě)回到XML文件中去了,下面我們來(lái)看看代碼的實(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è)類,就是用來(lái)處理XSLT變換的。
我們希望在上面的XML文件中加入一個(gè)新的link節(jié)點(diǎn),因而首先還是要讀入links.xml文件,構(gòu)建一個(gè)DOM樹(shù),然后再對(duì)這個(gè)DOM樹(shù)進(jìn)行修改(添加節(jié)點(diǎn)),最后把修改后的DOM寫(xiě)回到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),簡(jiǎn)化程序,我們把要加入的內(nèi)容硬編碼到記憶String對(duì)象中,而實(shí)際操作中,往往利用一個(gè)界面來(lái)提取用戶輸入,或者通過(guò)JDBC從數(shù)據(jù)庫(kù)中提取想要的內(nèi)容。
Text textseg;
Element link=doc.createElement("link");
首先應(yīng)該明了的是,無(wú)論什么類型的Node,Text型的也好,Attr型的也好,Element型的也好,它們的創(chuàng)建都是通過(guò)Document對(duì)象中的createXXX()方法來(lái)創(chuàng)建的(XXX代表具體要?jiǎng)?chuàng)建的類型),因此,我們要向XML文檔中添加一個(gè)link項(xiàng)目,首先要?jiǎng)?chuàng)建一個(gè)link對(duì)象:
Element linktext=doc.createElement("text");
textseg=doc.createTextNode(text);
linktext.appendChild(textseg);
link.appendChild(linktext);
……
創(chuàng)建節(jié)點(diǎn)的過(guò)程可能有些千篇一律,但需要注意的地方是,對(duì)Element中所包含的text(在DOM中,這些text也是代表了一個(gè)Node的,因此也必須為它們創(chuàng)建相應(yīng)的node),不能直接用Element對(duì)象的setNodeValue()方法來(lái)設(shè)置這些text的內(nèi)容,而需要用創(chuàng)建的Text對(duì)象的setNodeValue()方法來(lái)設(shè)置文本,這樣才能夠把創(chuàng)建的Element和其文本內(nèi)容添加到DOM樹(shù)中。看看前面的代碼,你會(huì)更好的理解這一點(diǎn):
doc.getDocumentElement().appendChild(link);
最后,不要忘記把創(chuàng)建好的節(jié)點(diǎn)添加到DOM樹(shù)中。Document類的getDocumentElement()方法,返回代表文檔根節(jié)點(diǎn)的Element對(duì)象。在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樹(shù)輸出了。這里的TransformerFactory也同樣應(yīng)用了工廠模式,使得具體的代碼同具體的變換器無(wú)關(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)容就被寫(xiě)入到文件中去了。
如何利用JSP開(kāi)發(fā)SAX應(yīng)用?
SAX是Simple API for XML的縮寫(xiě),它并不是由W3C官方所提出的標(biāo)準(zhǔn),可以說(shuō)是"民間"的事實(shí)標(biāo)準(zhǔn)。實(shí)際上,它是一種社區(qū)性質(zhì)的討論產(chǎn)物。雖然如此,在XML中對(duì)SAX的應(yīng)用絲毫不比DOM少,幾乎所有的XML解析器都會(huì)支持它。
這樣泛泛的說(shuō)來(lái)或許有些不容易理解,別急,后面的例子會(huì)讓你明白SAX的解析過(guò)程。看看這個(gè)簡(jiǎn)單XML文件:
<POEM>
<AUTHOR>Ogden Nash</AUTHOR>