<form id="hz9zz"></form>
  • <form id="hz9zz"></form>

      <nobr id="hz9zz"></nobr>

      <form id="hz9zz"></form>

    1. 明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

      Java XML圖文詳細教程(第5章)

      [摘要]來源:http://d23xapp2.cn.ibm.com/developerWorks/education/xml/xmljava/tutorial/xmljava-1-1.html第五章 解析器...
      來源:http://d23xapp2.cn.ibm.com/developerWorks/education/xml/xmljava/tutorial/xmljava-1-1.html

      第五章 解析器高級功能


      概覽

      我們已經討論了使用一個 XML 解析器來處理 XML 文檔的基礎。在本章節,我們將探討一些高級概念。

      首先,我們將從頭構建一棵 DOM 樹。換而言之,我們將不需要一個 XML 源文件來創建一個 Document 對象。

      然后,我們將向您顯示如何使用解析器來處理包含在一個字符串的 XML 文檔。

      接著,我們將向您顯示如何操作一棵 DOM 樹。我們將對我們示例的 XML 文檔操作并對其詩句排序。

      最后,我們將展示如何利用如 DOM 和 SAX 標準的接口使得解析器的更改十分容易。我們將向您展示兩個使用不同 XML 解析器的示例應用。而其中 DOM 和 SAX 代碼沒有改變。

      從頭構建一棵 DOM 樹

      有時您想要從頭構建一棵 DOM 樹。要完成這個任務,您創建一個 Document 對象,然后對其添加不同的 Node 對象。

      您可運行 java domBuilder 來看一個從頭構建一棵 DOM 樹的示例應用。該應用重新創建了通過對 sonnet.xml 最初解析而構建出的 DOM 樹(但不再創建空格符)。

      我們首先創建一個 DocumentImpl 類的實例。此類實現 DOM 定義的 Document 接口。
      Document doc = (Document)Class.
      forName("com.ibm.xml.dom.DocumentImpl").
      newInstance();


      domBuilder.java (代碼請參考附錄2)

      這段代碼不使用一個 XML 文檔來構建一個 DOM 樹。當樹被構建完后,該代碼將樹的內容輸出到標準輸出。

      ...
      <address>
      <name>
      <title>Mrs.</title>
      <first-name>Mary</first-name>
      <last-name>McGoon</last-name>
      </name>
      <street>1401 Main Street</street>
      <city>Anytown</city>
      <state>NC</state>
      <zip>34829</zip>
      </address>
      <address>
      <name>
      ...

      添加 Node 到我們的 Document

      現在我們有自己的 Document 對象了,我們開始創建 Node。我們第一個要創建的 Node 是一個 <sonnet> 元素。我們將創建所有的 Node,然后將每個節點添加到其對應的父母。

      注意我們使用 setAttribute 方法來對<sonnet>元素設置其 type 屬性的值。
      Element root = doc.
      createElement("sonnet");
      root.setAttribute("type",
      h"Shakespearean");
      構建您的文檔結構

      當我們開始構建我們的 DOM 樹時,我們將需要構建我們文檔的結構。要完成它,我們將需要恰當地使用 appendChild 方法。我們將創建 <author> 元素,然后創建在其下的其它元素,接著使用 appendChild 方法將所有這些元素添加到正確的父母。

      注意 createElement 是屬于 Document 類的一個方法。我們的 Document 對象擁有我們在此創建的所有元素。

      最終,注意到我們為所有的元素內容創建 Text 節點。Text 節點是元素的子女,而 Text 節點的父母則被添加到對應的父母下。

      Element author =
      doc.createElement("author");

      Element lastName = doc.
      createElement("last-name");
      lastName.appendChild(doc.
      createTextNode("Shakespeare"));
      author.appendChild(lastName);

      完成我們的 DOM 樹

      一旦我們對 <sonnet> 元素添加了所有內容,我們需要將它添加到 Document 對象。我們最后一次調用 appendChild 方法,這次是將子元素添加到 Document 對象上。

      記住一個 XML 文檔只能有一個根(root)元素;如果您要向Document添加多個根元素appendChild 將拋出一個異常。

      當我們構建好 DOM 樹后,我們構建一個 domBuilder 對象,然后調用它的 printDOMTree 方法來打印 DOM 樹。
      Element line14 = doc.
      createElement("line");
      line14.appendChild(doc.
      createTextNode("As any ..."));
      text.appendChild(line14);
      root.appendChild(text);

      doc.appendChild(root);

      domBuilder db = new domBuilder();
      db.printDOMTree(doc);

      使用 DOM 對象來避免解析

      您可以想象一個 DOM Document 對象作為一個 XML 文檔的編譯形式。如果您正使用 XML 來在不同方之間傳遞數據,您將可以通過接受和發送 DOM 對象而不是 XML 源數據來節約時間。

      這是最常見的原因您為何需要從頭來構建一個 DOM 樹。

      最壞情況下,您需要在您發送數據前從一棵 DOM 樹創建出 XML 源數據,然后在您接受 XML 數據時創建出一棵 DOM 樹。直接使用 DOM 對象將節約大量時間。

      一個警告:要注意一個 DOM 對象可能比 XML 源數據要大很多。如果您要在一個十分緩慢的連接線路上傳遞數據,發送較小的 XML 源數據而重新解析數據要比傳遞大數據更有效。

      解析一個 XML 字符串

      很有可能您需要解析一個 XML 字符串。IBM 的 XML4J 解析器支持這個功能,盡管您需要將您的字符串轉換成一個 InputSource 對象。

      第一步是從您的字符串中創建一個 StringReader 對象。一旦完成此步,您可以從 StringReader 創建一個 InputSource 對象。

      您可運行 java parseString 來查看代碼的運行結果。在示例應用中,XML 字符串是寫死的(hardcoded);有許多種方法讓您從一個用戶或其它機器獲得 XML 輸入。有了這個技術,您就不再需要將 XML 文檔輸出到一個文件系統來解析它了。

      parseString ps = new parseString();
      StringReader sr =
      new StringReader("<?xml version=\"1.0\"?>
      <a>AlphaBravo...");
      InputSource iSrc = new InputSource(sr);
      ps.parseAndPrint(iSrc);

      parseString.java (參附錄2)

      這段代碼展示了如何解析一個包含 XML 文檔的字符串。



      排列在一棵 DOM 樹中的 Node

      為了介紹您如何改變一棵 DOM 樹的結構,我們將修改我們的 DOM 示例來排列十四行詩的 <line> 元素。有多種 DOM 方法可以用來在 DOM 樹中搬移節點。

      要查看代碼的結果,運行 java domSorter sonnet.xml。它并不會改進詩的韻律,但它真確地排列了 <line> 元素。

      要開始排列工作,我們將使用 getElementsByTagName 方法來提取在文檔中的所有 <line> 元素。該方法節約了我們編寫代碼來遍歷整個樹的開銷。

      if (doc != null)
      {
      sortLines(doc);
      printDOMTree(doc);
      }
      ...
      public void sortLines(Document doc)
      {
      NodeList theLines =
      doc.getDocumentElement().
      getElementsByTagName("line");
      ...

      domSorter.java (參附錄2)

      這段代碼在 XML 文檔中查找所有的 <line> 元素,然后排序。它展示了如何操作一個 DOM 樹。



      提取我們的 <line> 文本

      為了簡化代碼,我們創建一個 helper 功能,getTextFromLine,用來提取包含在一對 <line> 元素中的文本。它簡單地找到 <line> 元素的第一個子女,如果其是一個 Text 節點就返回其文本。

      該方法返回一個 Java String 因此我們的排序過程可以使用 String.compareTo 方法來決定排序的次序。

      該段代碼實際上應該檢查 <line> 所有的子女,因為它們可能包含實體(entity)引用 (例如實體 &miss; 可能替代了文本 "mistress")。我們將把這個改進作為讀者的一個練習。

      public String getTextFromLine(Node
      lineElement)
      {
      StringBuffer returnString =
      new StringBuffer();
      if (lineElement.getNodeName().
      equals("line"))
      {
      NodeList kids = lineElement.
      getChildNodes();
      if (kids != null)
      if (kids.item(0).getNodeType() ==
      Node.TEXT_NODE)
      returnString.append(kids.item(0).
      getNodeValue());
      }
      else
      returnString.setLength(0);

      return new String(returnString);
      }

      文本排序

      現在我們有能力從一個給定的 <line> 元素獲取文本,我們可以開始排列數據了。由于我們只有 14 個元素,我們將使用冒泡排序。

      冒泡排序算法是比較兩個相鄰數據值,然后如果它們次序不對就交換它們。要完成交換,我們使用 getParentNode 和 insertBefore 方法。

      getParentNode 返回任意 Node 的父母;我們使用這個方法來獲得當前 <line> 的父母 (文檔的一個 <lines> 元素使用 sonnet DTD)。

      insertBefore(nodeA, nodeB) 在 nodeB 前插入 nodeA 到 DOM 樹中。insertBefore 最重要的特性是如果 nodeA 已經存在于 DOM 樹中了,它在插入 nodeB 前將刪除該節點。
      public void sortLines(Document doc)
      {
      NodeList theLines =
      doc.getDocumentElement().
      getElementsByTagName("line");
      if (theLines != null)
      {
      int len = theLines.getLength();
      for (int i=0; i < len; i++)
      for (int j=0; j < (len-1-i); j++)
      if (getTextFromLine(
      theLines.item(j)).
      compareTo(getTextFromLine(
      theLines.item(j+1))) > 0)
      theLines.item(j).
      getParentNode().insertBefore(
      theLines.item(j+1),
      theLines.item(j));
      }
      }

      用來操作樹的有用的 DOM 方法

      除insertBefore 之外,還有其它的 DOM 方法可用來操作樹。

      parentNode.appendChild(newChild)
      將一個節點作為給定父母節點的最后子女添加。調用 parentNode.insertBefore(newChild, null) 完成同樣功能。
      parentNode.replaceChild(newChild, oldChild)
      將 oldChild 用 newChild 來取代。節點 oldChild 必須是 parentNode 的子女。
      parentNode.removeChild(oldChild)
      將 oldChild 從 parentNode 下刪除。

      parentNode.appendChild(newChild);
      ...
      parentNode.insertBefore(newChild,
      oldChild);
      ...
      parentNode.replaceChild(newChild,
      oldChild);
      ...
      parentNode.removeChild(oldChild);
      ...

      關于樹操作還需注意的事項

      如果您需要刪除一個給定節點的所有子女,這要比看上去難多了。這兩段在左邊的示例代碼看起來可以完成任務。但是,在第二個才能完成。第一個示例代碼由于 kid 的實例數據在 removeChild(kid) 被調用后就改變了而無法完成任務。

      換而言之,for 循環刪除了 kid,第一個子女,然后檢查 kid.getNextSibling 是否為 null 。由于 kid 剛被刪除,它不再有任何同胞了,因此 kid.getNextSibling 是 null。 for 循環只會運行一次。不論 node 有一個或幾千個子女,第一段示例代碼只是刪除第一個子女。要使用第二段示例代碼來刪除所有的子節點。
      /** Doesn't work **/
      for (Node kid = node.getFirstChild();
      kid != null;
      kid = kid.getNextSibling())
      node.removeChild(kid);

      /** Does work **/
      while (node.hasChildNodes())
      node.removeChild(node.getFirstChild());


      使用另一個DOM 解析器

      盡管我們想象不出一個您為何要改換解析器的理由,您可以使用非 XML4J 的解析器來解析您的 XML 文檔。如果您查看t domTwo.java 的代碼,您將看到更換到 Sun 的 XML 解析器只需要兩個修改。

      首先,我們必須載入(import) Sun 公司的類。這很簡單。我們要修改的只是創建 Parser 對象的代碼。如您所見,Sun 的解析器構建過程比較復雜,但余下的代碼不用修改了。所有的 DOM 代碼不需要任何的修改。

      最終,在 domTwo 的不同之處是命令行格式。出于某些原因,Sun 的解析器不能用常用的方法來解析文件名。如果您運行 java domTwo file:///d:/sonnet.xml (當然根據您的系統修改 file URI),您將獲得 domOne 的相同結果。

      import com.sun.xml.parser.Parser;
      import
      com.sun.xml.tree.XmlDocumentBuilder;

      ...

      XmlDocumentBuilder builder =
      new XmlDocumentBuilder();
      Parser parser =
      new com.sun.xml.parser.Parser();
      parser.setDocumentHandler(builder);
      builder.setParser(parser);
      parser.parse(uri);
      doc = builder.getDocument();


      domTwo.java (參見附錄2)

      這段代碼等同于 domOne.java,但它使用 Sun 公司的 XML 解析器而不是 IBM 的。它展示了 DOM 接口的可移植性。



      使用一個不同的 SAX 解析器

      我們也編寫了 saxTwo.java 來展示如何使用 Sun 公司的 SAX 解析器。與 domTwo 類似,我們有兩個修改之處。第一個是載入(import) Sun 的 Resolver 類而不是 IBM 的 SAXParser 類。

      我們需要修改創建 Parser 對象的代碼,然后我們需要根據我們輸入的 URI 創建一個 InputSource 對象。我們要修改的只是創建 parser 的代碼需要被包含在 try 代碼段中以捕獲當我們創建 Parser 對象時可能產生的異常。

      import com.sun.xml.parser.Resolver;
      ...

      try
      {
      Parser parser =
      ParserFactory.makeParser();
      parser.setDocumentHandler(this);
      parser.setErrorHandler(this);
      parser.parse(Resolver.
      createInputSource(new File(uri)));
      }

      saxTwo.java (見附錄2)

      這段代碼等同于 saxOne.java,但它使用 Sun 公司的 XML 解析器而不是 IBM 的。它展示了 SAX 接口的可移植性。


      總結

      在本章節,我們介紹了一些使用 XML 解析器的高級編程技巧。我們展現了如何直接生成 DOM 樹,如何解析字符串而不是文件,如何在一棵 XML 樹中移動元素以及如何改變解析器而不用影響 DOM 和 SAX 的代碼。

      希望您喜歡本教程!

      這就是本教程的所有內容了。 我們討論了 XML 應用的基本架構,而且我們也介紹了您如何處理 XML 文檔。以后的教程將介紹構建 XML 應用更多的細節,包括:

      使用可視工具來構建 XML 應用
      將一個 XML 文檔從一種形式轉換到另一種
      為最終用戶或其他進程創建接口,及對后端存儲數據的接口
      要獲得更多信息

      如果您想了解更多的 XML 知識,可訪問 developerWorks 的 XML 專區。這個站點有示例代碼、其它的教程、關于 XML 標準的信息以及其它內容。

      最后,我們很愿意聽取您的意見!我們設計 developerWorks 是成為開發人員的資源。如果您有任何評價、建議或抱怨,請讓我們知道。

      謝謝,---Doug Tidwell 或 developerWorks 中國站點!




      日韩精品一区二区三区高清