DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 實用數據綁定: 深入考察 JAXB,第 2 部分:進一步考察 JAXB 的往返能力
實用數據綁定: 深入考察 JAXB,第 2 部分:進一步考察 JAXB 的往返能力
編輯:XML詳解     

數據綁定 API 允許通過編程操縱 XML,從這一點上說它非常有用。輸入 someElement.addAttribute("name", "value"); 要比解析文件、緩沖輸出、添加組成屬性聲明的字符、關閉流和刷新輸出流容易得多。但是如果不能正確地把變動寫回文件,所有這些操縱就沒有多少用處。本文重點討論數據綁定世界中所謂的 編組(marshalling)過程,特別是 JAXB 的編組能力。具體來說,您將了解 JAXB 如何在往返的舞台上取得成功。

  回顧

  本專欄的 第一篇中,您已經了解一些重要的術語: 編組和 解組(unmarshalling)是數據綁定世界固有的概念;不過還有一些新的術語,如 往返和 語義等價。往返是指從 XML 轉換成 Java 代碼然後再轉換回去的過程。數據綁定往返能力的質量通過輸入和輸出文檔匹配的程度衡量。語義等價使得比較成為可能,它允許丟棄 XML 中不重要的成分如可以忽略的空格,從而可以進行有效的比較。

  在 第 2 篇中,我介紹了一個簡單的 XML 文檔,在這裡再寫出來,如清單 1 所示。

清單 1. 吉他的基本 XML 清單(guitars.XML)

<guitars> 
 <guitar id="10021"> 
  <builder luthIEr="true">Ryan</builder> 
  <model>Mission Grand Concert</model> 
  <back-sides>Brazilian Rosewood</back-sides> 
  <top>Adirondack Spruce</top> 
  <notes> 
   <![CDATA[ 
    Just unbelIEvable...  this guitar has all the tone & 
    resonance you could ever want. I mean, <<WOW!!!>> This 
    is a lifetime guitar. 
   ]]> 
  </notes> 
 </guitar> 
 <guitar id="0923"> 
  <builder smallShop="true">Bourgeois</builder> 
  <model>OMC</model> 
  <back-sides>Bubinga</back-sides> 
  <top>Adirondack Spruce</top> 
 </guitar> 
 <guitar id="11091"> 
  <builder>Martin & Company</builder> 
  <model>OM-28VR</model> 
  <back-sides>Indian Rosewood</back-sides> 
  <top bearclaw="true">Sitka Spruce</top> 
  <notes>It's certainly true that Martin isn't the only game in town anymore. 
      Still, the OM-28VR is one of their best models...   and this one 
      has some fabulous bearclaw to boot.       Nice specimen of a 
      still-important guitar manufacturer. 
  </notes> 
 </guitar> 
</guitars> 

我還為這個文檔准備了一個模式,為了簡化起見這裡不再重復列出,要說明的是如何從這個模式生成 Java 源文件,如清單 2 所示。

清單 2. 生成的JAXB 類

C:\developerworks>xjc -p com.ibm.dw guitars.xsd -d src 
parsing a schema... 
compiling a schema... 
com\ibm\dw\impl\runtime\MSVValidator.Java 
com\ibm\dw\impl\runtime\SAXUnmarshallerHandlerImpl.Java 
com\ibm\dw\impl\runtime\ErrorHandlerAdaptor.Java 
com\ibm\dw\impl\runtime\AbstractUnmarshallingEventHandlerImpl.Java 
com\ibm\dw\impl\runtime\UnmarshallableObject.Java 
com\ibm\dw\impl\runtime\SAXMarshaller.Java 
com\ibm\dw\impl\runtime\XMLSerializer.Java 
com\ibm\dw\impl\runtime\ContentHandlerAdaptor.Java 
com\ibm\dw\impl\runtime\UnmarshallingEventHandlerAdaptor.Java 
com\ibm\dw\impl\runtime\SAXUnmarshallerHandler.Java 
com\ibm\dw\impl\runtime\ValidatorImpl.Java 
com\ibm\dw\impl\runtime\ValidatableObject.Java 
com\ibm\dw\impl\runtime\UnmarshallerImpl.Java 
com\ibm\dw\impl\runtime\NamespaceContext2.Java 
com\ibm\dw\impl\runtime\Discarder.Java 
com\ibm\dw\impl\runtime\NamespaceContextImpl.Java 
com\ibm\dw\impl\runtime\ValidatingUnmarshaller.Java 
com\ibm\dw\impl\runtime\UnmarshallingContext.Java 
com\ibm\dw\impl\runtime\GrammarInfoImpl.Java 
com\ibm\dw\impl\runtime\ValidationContext.Java 


 要保證生成並編譯了這些 Java 源文件以備使用。詳細的步驟請參閱本系列的 上一篇文章。

  XML 到 Java 代碼

  只要生成並准備好這些類,就可以把 清單 1中的 XML 文檔解組成 JAXB 在內存中的模型。這是測試 JAXB 往返能力的第一步。因為這不是一篇關於 JAXB 基礎的文章,我僅僅把代碼列在下面,如清單 3 所示。

清單 3. 解組 XML 到 Java 代碼

import Java.io.FileInputStream; 
import Javax.XML.bind.*; 
// Import generated classes 
import com.ibm.dw.*; 
public class RoundTripper { 
 private String inputFilename; 
 private String outputFilename; 
 private JAXBContext jc; 
 private final String PACKAGE_NAME = "com.ibm.dw"; 
 public RoundTripper(String inputFilename, String outputFilename) throws Exception { 
  this.inputFilename = inputFilename; 
  this.outputFilename = outputFilename; 
  jc = JAXBContext.newInstance(PACKAGE_NAME); 
 } 
 public Guitars unmarshal() throws Exception { 
  Unmarshaller u = jc.createUnmarshaller(); 
  return (Guitars)u.unmarshal(new FileInputStream(inputFilename)); 
 } 
 public static void main(String[] args) { 
  if (args.length < 2) { 
   System.err.println("Incorrect usage: Java RoundTripper" + 
          "[input XML filename] [output XML filename]"); 
   return; 
  } 
  try { 
   RoundTripper rt = new RoundTripper(args[0], args[1]); 
   Guitars guitars = rt.unmarshal(); 
  } catch (Exception e) { 
   e.printStackTrace(); 
   return; 
  } 
 } 
} 

注意:如果設置和運行這些類有問題,請參考本文的最後一節“ 運行示例程序”。

  在這裡一些人可能認為應該打印出內存中的版本。但是,用於打印內存中某些內容的 API 也可用於把數據寫入輸出流,因此這一步實際上不需要。

  Java 代碼到 XML

  現在可以要求 JAXB 把內存中的表示再返回到 XML。這樣就可以觀察輸入文件和輸出文件的區別。我已經向 RoundTripper 類中增加了一些代碼來完成這項工作,如清單 4 所示。

清單 4. 編組 Java 到 XML

import Java.io.FileInputStream; 
    import Java.io.FileOutputStream; 
import Javax.XML.bind.*; 
// Import generated classes 
import com.ibm.dw.*; 
public class RoundTripper { 
 private String inputFilename; 
 private String outputFilename; 
 private JAXBContext jc; 
 private final String PACKAGE_NAME = "com.ibm.dw"; 
 public RoundTripper(String inputFilename, String outputFilename) throws Exception { 
  this.inputFilename = inputFilename; 
  this.outputFilename = outputFilename; 
  jc = JAXBContext.newInstance(PACKAGE_NAME); 
 } 
 public Guitars unmarshal() throws Exception { 
  Unmarshaller u = jc.createUnmarshaller(); 
  return (Guitars)u.unmarshal(new FileInputStream(inputFilename)); 
 } 
     
 public void marshal(Guitars guitars) throws Exception { 
  Marshaller m = jc.createMarshaller(); 
  m.marshal(guitars, new FileOutputStream(outputFilename)); 
 } 
 public static void main(String[] args) { 
  if (args.length < 2) { 
   System.err.println("Incorrect usage: Java RoundTripper" + 
          "[input XML filename] [output XML filename]"); 
   return; 
  } 
  try { 
   RoundTripper rt = new RoundTripper(args[0], args[1]); 
   Guitars guitars = rt.unmarshal(); 
    
    rt.marshal(guitars); 
  } catch (Exception e) { 
   e.printStackTrace(); 
   return; 
  } 
 } 
}  

 同樣,這裡的代碼也相當簡單,意義明確。我使用 guitars.xml 作為輸入文件運行這個程序,並提供 output.xml 作為輸出文件名。沒有什麼輸出值得一提,和寫到終端上的文本一樣,不過執行這個過程您將得到一個新的文件(output.xml)。理論上講,這個文件應該是 guitars.XML 的完全復制,因為在內存中沒有改變該文件。

  拿蘋果和蘋果比較

  生成 output.XML 之後打開它。即使不完全相同,也應該和清單 5 類似。

清單 5. output.XML

<?XML version="1.0" encoding="UTF-8" standalone="yes"?> 
<guitars> 
 <guitar id="10021"> 
 <builder luthIEr="true">Ryan<builder> 
 <model>Mission Grand Concert</model> 
 <back-sides>Brazilian Rosewood<back-sides> 
 <top>Adirondack Spruce<top> 
 <notes> 
    
    Just unbelIEvable...  this guitar has all the tone & 
    resonance you could ever want. I mean, <<WOW!!!>> This 
    is a lifetime guitar. 
    
  <notes> 
 </guitar> 
 <guitar id="0923"> 
 <builder smallShop="true">Bourgeois</builder> 
 <model>OMC<model> 
 <back-sides>Bubinga<back-sides> 
 <top>Adirondack Spruce</top> 
 <guitar> 
 <guitar id="11091"> 
 <builder>Martin & Company<builder> 
 <model>OM-28VR<model> 
 <back-sides>Indian Rosewood<back-sides> 
 <top bearclaw="true">Sitka Spruce<top> 
 <notes>It's certainly true that Martin isn't the only game in town anymore. 
   Still, the OM-28VR is one of their best models...   and this one 
   has some fabulous bearclaw to boot.       Nice specimen of a 
   still-important guitar manufacturer. 
  <notes> 
 <guitar> 
<guitars> 

現在有了輸入和輸出文件,通過互相比較可以看看 JAXB 在往返中是如何工作的(原始文件如 清單 1所示)。

  增加 XML 聲明

  首先,注意到輸入文件沒有 XML 聲明(以 <xml version=... 開始的那一行)。JAXB 自動在輸出中插入這一行。這看起來似乎是一個小問題,但非常重要——現在常常需要在一個 XML 文檔中包含另一個 XML 文件,特別是在使用 SOAP 或者其他傳輸技術的時候。插入 XML 聲明的問題在與一個 XML 文檔只能有一個聲明。如果把 guitars.xml 插入另一個 XML 文檔,您並沒有違反這一規則;但是另一方面如果插入的是 output.XML,問題就出來了。因此現在 JAXB 又一個需要注意的特性。

  去掉 CDATA 節

  還要注意,原始 XML 文檔中的 CDATA 節被去掉了。從技術上講這並沒有違反語義等價的規則,兩個文檔的內容在語義上是相同的。第一個文檔使用 CDATA 避免實體引用,而在輸出文檔中為了支持實體引用而放棄了 CDATA。這更多的是文檔的事實等價問題而非 語義等價問題。雖然這不是一個重要的問題,但也應該注意。

  空白處理

  高興地看到正確地處理了空白。雖然去掉了 CDATA 節,但空白正確地保留了下來。此外,關於 Martin OM-28VR 吉他描述中的長空白也原樣保留了下來,這方面解決得很好。

  為了確認重新測試一遍

  評價往返能力最好的也是最有效的方式是 重新測試往返過程。但是要注意,我並不是說簡單地再進行一次測試。相反,這次把輸出文件(output.xml)提供給往返程序作為 輸入文件。如果說這個過程向 XML 文件中引入了不應該有的什麼內容,那就是每次後續的往返創建的輸出都和原始文件(guitars.XML)離得更遠一點。這對於隔離問題是一種很好的方式。好的數據綁定工具應該能夠一遍一遍地總是創建相同的文件,特別是在最初的往返過程之後。

在這一步中,我要求 RoundTripper 生成 retest.xml,以 output.xml 作為源 XML。結果如清單 6 所示。

清單 6. retest.XML

<XML version="1.0" encoding="UTF-8" standalone="yes"?> 
<guitars> 
 <guitar id="10021"> 
 <builder luthIEr="true">Ryan<builder> 
 <model>Mission Grand Concert</model> 
 <back-sides>Brazilian Rosewood<back-sides> 
 <top>Adirondack Spruce<top> 
 <notes> 
    
    Just unbelIEvable...  this guitar has all the tone & 
    resonance you could ever want. I mean, <<WOW!!!>> This 
    is a lifetime guitar. 
    
  <notes> 
 <guitar> 
 <guitar id="0923"> 
 <builder smallShop="true">Bourgeois<builder> 
 <model>OMC<model> 
 <back-sides>Bubinga<back-sides> 
 <top>Adirondack Spruce<top> 
 <guitar> 
 <guitar id="11091"> 
 <builder>Martin & Company</builder> 
 <model>OM-28VR<model> 
 <back-sides>Indian Rosewood<back-sides> 
 <top bearclaw="true">Sitka Spruce<top> 
 <notes>It's certainly true that Martin isn't the only game in town anymore. 
   Still, the OM-28VR is one of their best models...   and this one 
   has some fabulous bearclaw to boot.       Nice specimen of a 
   still-important guitar manufacturer. 
  <notes> 
 </guitar> 
<guitars> 


好消息是 清單 5和 清單 6完全相同,這說明 JAXB 經過最初的往返步驟之後可以很好地工作。

  總的來說 JAXB 表現得很好。雖然我認為自動增加 XML 聲明確實是一個問題,但和影響內容的 API 相比不算很糟。JAXB 還以和預期稍有不同的方式處理 CDATA 節,但確實保持了語義等價。下一篇文章中,我將介紹進一步影響輸出文件的各種選項,手工解決 JAXB 造成的一些問題。但總而言之,JAXB 證明自己能夠很好地按照期望保持輸入文檔。

  運行示例程序

  最後讓我們來分享我的一個秘技,用於輕松制作 classpath 和 JAXB 示例的 Ant 設置。清單 7 是我在本文中使用的 Ant 構建文件。您要使用這個文件,只需要把路徑改為您自己的 XML 輸入文件,以及您的 JAXB JAR 文件。

清單 7. Ant 構建文件

<?XML version="1.0"?> 
<project basedir="." default="roundtrip"> 
 <property name="jwsdp.home" value="c:\jwsdp-1.3"/> 
 <property name="xml.inputFile" value="guitars.XML"/> 
 <property name="xml.outputFile" value="output.XML"/> 
 <property name="xml.retestFile" value="retest.XML"/> 
 <path id="classpath"> 
 <pathelement path="build"/> 
 <fileset dir="${jwsdp.home}" includes="jaxb/lib/*.jar"/> 
 <fileset dir="${jwsdp.home}" includes="jwsdp-shared/lib/*.jar"/> 
 <fileset dir="${jwsdp.home}" includes="jaxp/lib/**/*.jar"/> 
 <path> 
 <taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask"> 
 <classpath refid="classpath"/> 
 <taskdef> 
 <!-- compile Java source files --> 
 <target name="compile"> 
 <!-- generate the Java content classes from the schema --> 
 <echo message="Compiling the schema external binding file..."/> 
 <xjc schema="guitars.xsd" package="com.ibm.dw" target="src"/> 
 <!-- compile all of the Java sources --> 
 <echo message="Compiling the Java source files..."/> 
 <Javac srcdir="src" destdir="build" debug="on"> 
  <classpath refid="classpath"/> 
 </Javac> 
  
 <!-- Copy over the propertIEs files --> 
 <copy todir="build"> 
  <fileset dir="src"> 
   <exclude name="**/*.Java"/> 
  </fileset> 
 <copy> 
 <target> 
 
 <target name="roundtrip" depends="compile"> 
  <echo message="Converting XML file to Java and back..."/> 
  <Java classname="RoundTripper"> 
   <arg value="${XML.inputFile}" /> 
   <arg value="${XML.outputFile}" /> 
   <classpath refid="classpath" /> 
  </Java> 
 <target> 
 
 <target name="roundtrip-retest" depends="roundtrip"> 
  <echo message="Converting XML file to Java and back... (Second iteration)"/> 
  <Java classname="RoundTripper"> 
   <arg value="${XML.outputFile}" /> 
   <arg value="${XML.retestFile}" /> 
   <classpath refid="classpath" /> 
  <Java> 
 <target> 
<project> 

  默認情況下,這個文件將從模式生成源文件、編譯這些源文件並復制需要的 JAXB 屬性文件,然後編譯並運行 RoundTripper 類。您可以手工運行 roundtrip-retest 目標,它執行第二遍往返過程,使用 output.XML 作為輸入文件。該文件會使生活輕松許多,但願您喜歡它!






 
XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved