用于XML的簡單API
Eric Armstrong
本章主要討論Simple API for XML (SAX),它是一種事件驅動、串行存取XML文檔的機制。這是多數servlet和面向網絡的程序用來傳送和接收XML文檔的協議,因為在目前XML文檔處理的可用機制中它最快并且所需內存最少。 SAX協議比文檔對象模型(DOM)需要進行更多的編程。它是事件驅動模型(你提供回調方法,解析器在讀取XML數據的時候調用它們),因此很難實現可視化。最后,不能倒退回文檔的前面部分或重新組織它,只能回退到一個串行數據流或重新組織從流中讀取的字符。 由于上述原因,如果開發者編寫面向用戶的應用程序,且該應用程序顯示XML文檔并可能要對它進行修改,那么就應該使用教程下一部分介紹的DOM機制,文檔對象模型。 然而,即便是專門建立DOM應用程序,仍然要熟悉SAX模型,主要原因如下: 在解析DOM文檔時,會生成相同類型的異常,所以JAXP SAX和DOM應用程序的錯誤處理程序是相同的。 默認情況下, 規范要求忽略驗證錯誤(在該教程的這一部分中會詳細介紹) 。如果想要在驗證錯誤事件中拋出一個異常(并且能這樣做),那么就需要理解SAX錯誤處理的工作原理。 可以看到在本教程的DOM節,可以使用一種機制將現有數據集轉換成XML——然而,要充分利用該機制首先必須理解SAX模型。 注意:可以在下面的地址中找到本章的例子: 何時使用 SAX
當要快速、高效地讀取XML數據時,SAX是無可挑剔的。它對內存的要求很小,因為它不構建XML數據的內部表示(樹結構)。它僅僅將讀取的數據發送給應用程序——然后應用程序就可以隨意處理它所見到的數據。 實際上,SAX API類似于串行I/O流。在流進入的時候,可以看到數據,但是不能回到以前的位置或跳躍到不同的位置。總的說來,如果僅想讀取數據并在這基礎上運行應用程序它就非常有效。 理解SAX事件模型對于將現有數據轉換成XML非常有用。在從任意數據結構生成XML 可以看到,轉換過程的關鍵是修改現有應用程序,以便在讀取數據時發送合適的SAX事件。 但是,當需要修改XML結構時——特別是需要互相修改的時候,使用類似于文檔對象模型(DOM)的內存結構更為合理。 然而,雖然DOM提供了許多處理大型文檔(如書和論文)的強大功能,但是還是需要進行復雜的編程 (在何時使用DOM中詳細介紹了該處理的細節) 。 在簡單的應用程序中,這么復雜是完全沒有必要的。在快速開發和簡單的應用程序中,使用面向對象的XML編程標準更加合理,在JDOM 和dom4j中會具體介紹。 編寫簡單的XML文件
現在開始編寫一個用來進行幻燈片播放的XML數據的簡單版本。在該練習中,使用文本編輯器創建數據,這樣就能比較適應XML文件的基本格式。你將使用該文件并在以后的練習中對它進行擴展。 創建文件
使用標準的文本編輯器,創建一個叫做 注意: 這里已經有一個 編寫聲明
下一步,編寫聲明,其中該聲明將文件標識為一個XML文檔。聲明以字符"<?"開始 <?xml version='1.0' encoding='utf-8'?> 該行表明此文檔是一個XML文檔,它遵守XML1.0版本規范,并且使用8-位Unicode字符編碼方案。(要想獲得編碼方案的信息,請查看Java編碼方案 .) 由于沒有指定文檔是“獨立的”,解析器假設該文檔可能包含到其他文檔的引用。想知道如何將一個文檔指定為“獨立的” 。 添加注釋
注釋會被XML解析器忽略。實際上,你根本就看不到它們,除非你激活解析器里的特殊設置。教程后面部分的處理詞法事件中會詳細介紹如何使用它。現在,添加下面突出顯示的內容,在文件中增加一條注釋。 <?xml version='1.0' encoding='utf-8'?>
定義根(Root)元素
在聲明之后,每個XML文件都會精確地定義一個元素,這就是根元素。文件中的任何其他元素都包含在該元素中。輸入下面突出顯示文本,定義該文件的根元素 <?xml version='1.0' encoding='utf-8'?> <!-- A SAMPLE set of slides -->
注意:XML元素命名是大小寫敏感的。結束標簽必須和起始標簽匹配。 給元素添加屬性
幻燈片播放有大量相關數據項,它們都不需要任何結構。所以可以將它們定義成 ... <slideshow
> </slideshow> 在創建標簽或屬性的名字時,除了字符和數字之外還可以使用連字符 ( 注意: 要慎重使用冒號或避免一起使用,因為在定義XML文檔的命名空間的時候也要使用它。 添加嵌套元素
XML能夠表示層次結構化數據,這意味著一個元素可以包含其他元素。添加下面突出顯示的文本,定義一個幻燈片(slide)元素,并在它內部包含一個標題(title)元素: <slideshow ... >
</slideshow> 這里,也給幻燈片(slide)添加了一個 然而,更重要的是,該例子顯示了適合定義成元素( 添加 HTML-風格文本
由于XML允許你定義任何你想定義的標簽,因此可以定義一組看上去類似于HTML的標簽。實際上這是通過XHTML標準實現的。在SAX教程的結束部分,你會進一步了解它。現在,輸入下面突出顯示的文本,定義一個具有兩個列表項目的幻燈片,這些項目使用HTML-風格的<em>標簽進行強調 (通常使用斜體字): ... <!-- TITLE SLIDE --> <slide type="all"> <title>Wake up to WonderWidgets!</title> </slide>
</slideshow> 后面會看到,如果XHTML元素使用了跟定義的title 元素相同的名字,兩者就會發生沖突。在講解解析參數化的DTD時,會具體討論沖突產生機制 (DTD)和一些可用的解決方案。 添加空元素
HTML和XML的一個主要區別在于,所有的XML必須是形式良好的(well-formed)——這意味著每個標簽必須有結束標簽或為空標簽。現在,你會很滿意于使用結束標簽。添加下面突出顯示的文本,定義一個沒有內容的空列表項元素: ... <!-- OVERVIEW --> <slide type="all"> <title>Overview</title> <item>Why <em>WonderWidgets</em> are great</item>
<item>Who <em>buys</em> WonderWidgets</item> </slide> </slideshow> 注意,任何元素都可以是空元素。它所做的是用"/>" 而不是">"來結束標簽。輸入 注意:使得XML形式良好的另一個因素是合適的嵌套。所以 最終產品
<?xml version='1.0' encoding='utf-8'?> <!-- A SAMPLE set of slides --> <slideshow title="Sample Slide Show" date="Date of publication" author="Yours Truly" > <!-- TITLE SLIDE --> <slide type="all"> <title>Wake up to WonderWidgets!</title> </slide> <!-- OVERVIEW --> <slide type="all"> <title>Overview</title> <item>Why <em>WonderWidgets</em> are great</item> <item/> <item>Who <em>buys</em> WonderWidgets</item> </slide </slideshow> 現在已經創建了一個可以使用的文件,下面準備編寫程序以使用SAX解析器回送它。在下一節完成該工作。 使用SAX解析器回送XML文件
在實際應用中,沒有什么必要使用SAX解析器回送XML文件。通常,希望通過某種方式處理數據,以便能夠有效地利用它。(如果想要回送它,可以建立DOM樹,并用它來輸出結果。)但是,回送XML結構是查看運行中的SAX解析器的很好的方法,而且它在調試中也特別有用。 在本練習中,將把SAX解析器事件回送到 注意:本節討論的代碼在 創建框架
{ public static void main(String argv[]) { } } 由于準備單獨運行它,所以需要一個main方法。并且需要命令行參數,這樣就能告訴應用程序回送哪個文件。 導入類
import java.io.*; import org.xml.sax.*; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; public class Echo { ... 當然, 設置I/O
業務的第一件事是處理命令行參數,得到要回送的文件的名字,并建立輸出流。添加下面突出顯示的文本,以處理這些任務并且做一些內務工作: public static void main(String argv[]) {
}
創建輸出流書寫器時,選擇的是UTF-8字符編碼。也可以選擇US-ASCII或UTF-16,Java平臺也支持它們。如果想獲得關于這些字符集的更多信息,請查看Java編碼方案。 實現ContentHandler接口
滿足我們當前需要的最重要的接口是 實現該接口的最簡單的方法是擴展 public class Echo { ... } 注意:
接口需要所有的這些方法來拋出 遇到起始標簽或結束標簽時,將標簽的名字作為 設置解析器
現在(終于)要設置解析器了。添加下面突出顯示的文本,設置并啟動它: public static void main(String argv[]) { if (argv.length != 1) { System.err.println("Usage: cmd filename"); System.exit(1); }
try { // out = new OutputStreamWriter(System.out, "UTF8");
} catch (Throwable t) { t.printStackTrace(); } System.exit(0); } 通過上面幾行代碼,創建了一個 注意: 這時,你只是在簡單地捕獲解析器可能拋出的任何異常。在教程的使用非驗證解析器處理錯誤一節中將會了解到錯誤處理的更多信息。 編寫輸出
... 調用emit時,I/O錯誤和它的標識信息一起包裝在 間隔輸出
這是進行具體處理之前要建立的另一個基礎結構。添加下面突出顯示的代碼,定義一個 private void emit(String s) ... }
} 注意: 雖然這看起來有點多余,但還需要在前面的代碼中多次調用 處理內容事件
最后,編寫一些實際處理 文檔事件
將下面突出顯示的代碼添加到start-document 和end-document 事件中: static private Writer out;
private void echoText() ... 這里,當解析器遇到文檔開始時,回送XML聲明。由于使用UTF-8建立 注意:然而, IO類不理解帶有帶有連字符號的編碼名,所以使用“UTF8”而不用“UTF-8”。 在文檔最后,只要放置最終新行并刷新輸出結果流。不需要進行很多操作。 元素事件
現在做件有趣的事情。添加下面突出顯示的代碼處理啟始元素(start-element)和結束元素(end-element)事件:
private void emit(String s) ... 使用該代碼,回送元素標簽,包括在起始標簽中定義的任何屬性。注意調用 字符事件
不要求解析器同時返回所有特定字符。解析器一次可以返回單個字符,也可以返回幾千個字符,但實現中仍然要遵守標準。所以,如果應用程序要處理遇到的字符,最好將這些字符積累在緩沖區中,并且僅在你認為已經找出了所有的字符時才處理它們。 public class Echo01 extends DefaultHandler { public static void main(String argv[]) { ... 然后,添加下面突出顯示的代碼,積累解析器傳遞到緩沖中的字符: public void endElement(...) throws SAXException { ... }
private void emit(String s) ... 接著,添加下面突出顯示的方法,將緩沖中的內容發送到輸出流中。 public void characters(char buf[], int offset, int len) throws SAXException { ... }
private void emit(String s) ... 當同一行兩次調用該方法(下面可以看到,這種情況經常發生),緩沖區為空。在這種情況下,方法僅僅返回。然而,如果緩沖區非空,將它的內容發送到輸出流中。 最后,添加下面的代碼,以便能在開始或結束一個元素的時候,回送緩沖區的內容: public void startElement(...) throws SAXException { String eName = sName; // element name ... } public void endElement(...) throws SAXException { String eName = sName; // element name ... } 當然,元素結束時,也完成了文本積累。所以,這時就可以回送了,以便在開始下一個元素之前清除緩沖區。 但是,在開始一個元素的時候,也希望回送積累的文本。這對于文檔風格的數據來說很有必要,這種類型的數據包含混雜了文本的XML元素。例如,在該文檔段中:
ideas. 初始文本“This paragraph contains”被元素 注意:在大多數時候,出現 恭喜!這時你已經編寫了一個完整的SAX解析器應用程序。下一步是編譯并運行它。 注意:為了確保精確度,字符處理程序必須掃描緩沖區看看有沒有字符(' 編譯并運行程序
在Java WSDP中,JAXP庫發布在目錄 注意:由于將JAXP 1.1 建立在Java 2平臺的1.4版本中,你也可以執行大多數的JAXP 教程部分(SAX、 DOM和XSLT),不需要安裝特殊的JAR 文件。然而,要使用JAXP中的新性能—— XML模式和XSLTC編譯轉換器——需要按照發行注意事項中的說明安裝JAXP 1.2。 在Java 2平臺的版本1.2和1.3中,可以執行下列命令編譯并運行程序: javac -classpath java -cp 或者,也可以將JAR文件放置在平臺擴展目錄下,并使用更加簡單的命令: javac Echo.java java Echo slideSample.xml 在Java 2平臺的1.4版本中,必須將JAR文件視作建立在Java 2平臺上的“支持標準”(endorsed standard)的新版本。這就需要將JAR文件放在支持標準目錄 javac Echo.java java Echo slideSample.xml 注意: 也可以精心在命令行上設置 檢查輸出
<slideshow title="Sample Slide Show" date="Date of publication" author="Yours Truly"> <slide type="all"> <title>Wake up to WonderWidgets!</title> </slide> ... 注意:程序的輸出保存在 查看該輸出,會發現大量問題。就是多余的垂直空白是哪里來的?并且為什么代碼沒有進行處理,元素就能夠很好地縮進?稍后就來回答這些問題。首先,需要注意結果的一些方面: · 在文件頂部定義的注釋<!-- A SAMPLE set of slides -->沒有出現在列表中。忽略注釋,除非實現 · 所有的元素屬性都列在一行中。如果窗口不夠寬,就看不到所有內容。 · 定義的單標簽空元素 ( 識別事件
該版本的回送(Echo)程序對于顯示XML非常有用,但是它沒有告訴你解析器內部究竟在做些什么。下一步是修改該程序,這樣你就能看到空白和垂直行是怎么來的。 注意:本節討論的代碼在 public void startDocument() throws SAXException {
emit("<?xml version='1.0' encoding='UTF-8'?>"); nl(); } public void endDocument() throws SAXException {
try { ... } public void startElement(...) throws SAXException { echoText();
String eName = sName; // element name if ("".equals(eName)) eName = qName; // not namespaceAware emit("<"+eName); if (attrs != null) { for (int i = 0; i < attrs.getLength(); i++) { String aName = attrs.getLocalName(i); // Attr name if ("".equals(aName)) aName = attrs.getQName(i); emit(" "); emit(aName+"=\""+attrs.getValue(i)+"\"");
} } emit(">"); } public void endElement(...) throws SAXException { echoText();
String eName = sName; // element name if ("".equals(eName)) eName = qName; // not namespaceAware emit("<"+eName+">"); } ... private void echoText() throws SAXException { if (textBuffer == null) return;
String s = ""+textBuffer emit(s); textBuffer = null; } 編譯并運行該版本的程序,以輸出更多信息。現在每行顯示一個屬性。但是,更加重要的是,它輸出類似于下面的行: | 表明縮進空間和分隔屬性的新行來自于解析器傳遞給 注意:XML規范要求所有輸入行分隔符都放在單個新行中。Java、C和UNIX系統都有自己的新行字符,但是在Windows系統中使用別名“linefeed”。 壓縮輸出結果
注意:本節討論的代碼在 public void echoText() throws SAXException { nl(); emit("CHARS: |");
String s = ""+textBuffer;
emit("|"); } public void characters(char buf[], int offset, int len) throws SAXException {
String s = new String(buf, offset, len); ... } 如果現在運行程序,你可以發現你已經消除了縮進,這是因為縮進的空間是元素前面的空白的一部分。添加下面突出顯示的代碼管理縮進: static private Writer out;
... public void startElement(...) throws SAXException { nl(); emit("ELEMENT: "); ... } public void endElement(...) throws SAXException { nl(); emit("END_ELM: "); emit("</"+sName+">"); } ... private void nl() throws SAXException { ... try { out.write(lineEnd);
} catch (IOException e) { ... } 該代碼建立縮進字符串,跟蹤當前的縮進層,并且在調用n1方法的時候,輸出縮進字符串。如果將縮進字符串設置為"",輸出結果就不縮進 (試驗一下,你會明白為什么需要添加縮進) 當你知道你已經看到了添加到回送(Echo)程序的“機械”代碼的最后部分,你會覺得很開心。從這里開始,你所作的事情都會進一步幫你理解解析器的工作原理。目前所進行的步驟,告訴了你解析器是如何查看它處理的XML數據的。它也給你提供了一個有效的調試工具,幫助你理解解析器看到的內容。 檢查輸出
ELEMENT: <slideshow ... > CHARS: CHARS: ELEMENT: <slide ... END_ELM: </slide> CHARS: CHARS: 注意:完整的輸出結果在 注意, 同一行兩次調用 同樣也要注意, 然而,沒有DTD時,解析器必須假設它能看到的所有元素包含文本,類似于概述幻燈片的第一項元素: <item>Why <em>WonderWidgets</em> are great</item>
CHARS: Why ELEMENT: <em> CHARS: WonderWidgets END_ELM: </em> CHARS: are great END_ELM: </item> 文檔和數據
在本例中,很明顯有些字符結合了元素的層次結構。文本可以包圍元素這個事實(或避免在DTD或模式中這樣做)能夠解釋為什么有的時候聽到別人討論"XML數據"而在其他時候則聽到別人討論"XML文檔"。XML能夠處理結構化數據和包含標簽的文本文檔。兩者之間的區別在于元素之間是不是允許有文本。 注意:在本教程的下面部分,要使用 添加其他事件處理程序
除 識別文檔的位置
locator 是一個對象,它包含了查找文檔所必需的信息。 也可以使用定位器打印診斷信息。除了文檔的位置和公共標識符外,定位器包含給出最近使用的列和行的數目的方法。但是,僅在解析器的開始部分調用一次 注意:本節討論的代碼在 public void characters(char buf[], int offset, int len) throws SAXException { if (textBuffer != null) { echoText(); textBuffer = null; } String s = new String(buf, offset, len); ... } 然后,將下面的方法添加到Echo程序中,獲得文檔定位器,并使用它回送文檔的系統ID。 ... private String indentString = " "; // Amount to indent private int indentLevel = 0;
public void startDocument() ... · 同其他 · 這些方法的拼寫是" 在 LOCATOR SYS ID: file: START DOCUMENT <?xml version='1.0' encoding='UTF-8'?> ... 這里,很明顯 處理處理指令
有時在XML數據中編寫特定于應用程序的處理指令很有意義。在該練習中,在 注意:本節討論的代碼在 從理解XML中可以得知,處理指令的格式是 <slideshow ... >
<!-- TITLE SLIDE --> · 處理指令的"data"部分可以包含空格也能為空。但是在初始 · 利用完整的Web-unique包前綴來完全限定目標很有意義,所以避免了它跟其他可能處理相同數據的程序沖突。 · 為了提高可讀性,最好在應用程序名后加一個冒號(:),如下所示: <?my.presentation.Program: QUERY="..."?> 該冒號使得目標名成為"label",它識別指令的目標接收者。然而,w3c規范允許目標文件中有":" ,但是IE5的一些版本卻不支持,會出錯。本教程避免在目標名中使用冒號。 現在可以使用一個處理指令,將下面的代碼添加到Echo應用程序中: public void characters(char buf[], int offset, int len) ... }
private void echoText() ... 完成編輯后,編寫并運行程序。輸出結果的相關部分看起來如下所示: ELEMENT: <slideshow ... > PROCESS: <?my.presentation.Program QUERY="exec, tech, all"?> CHARS: ... 小結
在小異常 使用非驗證解析器處理錯誤
該版本的Echo程序使用非驗證解析器。所以它不能確定XML文檔是否包含了正確的標簽,或這些標簽是否在正確的序列中。換句話說,它不能告訴你文檔是否有效。然而,它能判斷出文檔結構是否良好。 本節中,要修改幻燈片顯示(slideshow)文件,以生成各類錯誤,并觀察解析器是如何處理它們的。你能夠看出默認情況下哪些錯誤情形是可以忽略的,也可以看到如何處理它們的。 生成錯誤
解析器可以產生三類錯誤:致命錯誤、錯誤和警告。在本練習中,我們將修改XML文件以產生一個致命錯誤。然后查看Echo應用程序是如何處理它的。 注意:本練習中要創建的XML結構在 產生致命錯誤的一個最簡單的方法就是刪除空 1. 將 ... <!-- OVERVIEW --> <slide type="all"> <title>Overview</title> <item>Why <em>WonderWidgets</em> are great</item> <item/> <item>Who <em>buys</em> WonderWidgets</item> </slide> ... ... <item>Why <em>WonderWidgets</em> are great</item> <item> <item>Who <em>buys</em> WonderWidgets</item> ...
... at org.apache.xerces.parsers.AbstractSAXParser... ... at Echo.main(...) 注意:上面的代碼是JAXP 1.2 庫產生的。如果使用不同的解析器,錯誤消息可能會有所不同。 當出現致命錯誤時,解析器就不能繼續下去。所以,如果應用程序沒有產生異常(一會兒你就會看到怎么做了),那么默認的錯誤-事件處理程序會產生一個致命錯誤。棧跟蹤是由main方法中的 ... } catch (Throwable t) {
} 棧跟蹤不是非常有用。然后,你會看到出現錯誤時如何產生更好的診斷信息。 處理SAXParseException
遇到錯誤時,解析器產生 注意:本練習中要創建的代碼在
} catch (Throwable t) { t.printStackTrace(); } 這時運行程序將產生一個錯誤消息,該錯誤消息非常有用,格式如下: ** Parsing error, line 22, uri file:<path>/slideSampleBad1.xml The element type "item" must be ... 注意:錯誤消息的文本取決于使用的解析器。該消息是用JAXP 1.2產生的。 注意:在產品應用程序中不適合這樣捕獲異常。現在開始逐步建立完整的錯誤處理程序。另外,它能捕獲所有的空指針異常,當傳遞給解析器空值時,會拋出該異常。 處理SAXException
有時解析器能夠產生一個更加通用的 public void startDocument() throws SAXException 所有的
private void emit(String s) throws SAXException { try { out.write(s); out.flush(); } } } 注意: 如果在調用 當解析器將異常發送回調用解析器的代碼,可以使用原始異常產生棧跟蹤。添加下面的代碼實現該功能: ... } catch (SAXParseException err) { System.out.println("\n** Parsing error" + ", line " + err.getLineNumber() + ", uri " + err.getSystemId()); System.out.println(" " + err.getMessage());
} catch (Throwable t) { t.printStackTrace(); } 該代碼測試 改進SAXParseException處理程序
由于 ... } catch (SAXParseException err) { System.out.println("\n** Parsing error" + ", line " + err.getLineNumber() + ", uri " + err.getSystemId()); System.out.println(" " + err.getMessage());
} catch (SAXException sxe) { // Error generated by this application // (or a parser-initialization error) Exception x = sxe; if (sxe.getException() != null) x = sxe.getException(); x.printStackTrace(); } catch (Throwable t) { t.printStackTrace(); } 現在程序要處理它看到的任何SAX解析異常。可以看到解析器產生致命錯誤的異常。但是對于非致命性錯誤和警告,默認的錯誤處理程序不會產生異常,也不會顯示任何消息。下面,學進一步學習錯誤和警告,并查看如何提供錯誤處理程序處理它們。 處理 ParserConfigurationException
最后,回想一下,如果類 } catch (SAXException sxe) { Exception x = sxe; if (sxe.getException() != null) x = sxe.getException(); x.printStackTrace();
} catch (Throwable t) { t.printStackTrace(); 不可否認,這里有相當多的錯誤處理程序。但是現在你至少知道了可能出現的異常類型。 注意:如果不能找到系統屬性指定的factory類或不能將它實例化,也可拋出 處理 IOException
} catch (ParserConfigurationException pce) { // Parser with specified options can't be built pce.printStackTrace();
} catch (Throwable t) { ... 讓 處理非致命錯誤
當XML文檔不能滿足有效性約束的時候,會出現非致命 錯誤。如果解析器發現文檔無效,就會產生一個錯誤事件。給定一個DTD 或模式,當文檔具有無效標簽,或標簽出現在不應該出現的地方,或元素包含無效數據(模式的情況中),驗證解析器就會產生該錯誤。 實際上到現在為止還沒有涉及到有效性問題。但是,由于現在討論的是錯誤處理這一主題,所以現在可以編寫錯誤處理代碼。 理解非致命性錯誤的最重要的原則是在默認情況下,它們是可以忽略的。 但是如果在文檔中出現有效性錯誤,你可能不樂意繼續處理它。你可能希望將它們作為致命錯誤處理。在下面編寫的代碼中,建立錯誤處理程序實現該功能。 注意:本練習中創建的程序的代碼在 為了接管錯誤處理,覆蓋方法 添加下面的代碼,覆蓋錯誤處理程序: public void processingInstruction(String target, String data) throws SAXException { ... }
注意:查看 處理警告
默認情況下,警告也被忽略。警告能提供很多信息,并且需要一個DTD。例如,如果在DTD中兩次定義了一個元素,產生一個警告——它并不是不合法的,并且不會引起任何問題,但是你可能希望知道這些,因為它可能不是故意的。 // treat validation errors as fatal public void error(SAXParseException e) throws SAXParseException { throw e; }
|
posted on 2005-03-23 15:19 辰 閱讀(303) 評論(0) 編輯 收藏 所屬分類: Java_Xml