1、使用 JAXB 处理 XML 文档使用 JAXB 处理 XML 文档先睹为快JAXB 以其方便的 XML 数据处理能力可能会引起你的兴趣。你可能还不了解JAXB 是什么,想要知道它到底有什么好处,如果这是你需要的,你才会再花时间去细细的研究它,或者你只需要使用最基本的功能。然而 Sun 关于 JAXB 的文档有 80 页之多。我想大部分人都没有耐心看完这样的长篇大论。本文以简短的篇幅介绍了 JAXB 的基本使用,算是先睹为快吧。本文附带的代码包括了 JAXB1.0 early access 版本和本文所使用的代码。欢迎与我讨论: mailto:本文假设你会使用 Java 编程,了解并能够看懂
2、XML,DTD。1 为什么要使用 JAXB在 Java 中处理 XML 数据的常规方法有 SAX,DOM 等。其中 SAX 使用起来很麻烦,不能修改 XML 数据;而 DOM 的处理大文档速度非常的慢,易用性也不必 SAX 好到哪里去。实际上,无论是 SAX 还是 DOM 都不是专门为 Java 准备的,它们都是访问 XML 文档的统一底层接口,与语言无关。现在我们有了另外的选择。这就是 JAXB 和 JDOM。JDOM 与本文无关,目前最新的版本是 beta8,感兴趣的话,可以访问 http:/www.jdom.org/。JAXB 的全名是 Java Architecture for XML
3、 Binding,目前是 1.0 的 early access 版本,在 Sun 的 Java 站点只有注册为成员才能够下载。JAXB 的特点就是将你用 DTD 定义好的 XML 文档映射为 Java 对象,提供简单、快速的数据操作方式。要访问 XML 中的元素、属性只要通过相应对象上的一系列 getter 和 setter 方法。你还可以通过 marshal 方法将对象的数据写进 XML 文件,通过 unmarshal 方法将 XML 文件的数据读入对象,通过validate 方法验证 XML 文件是否符合 DTD 的约束。JAXB 的缺点就在于只能访问特定的(也就是你用 DTD 定义的)
4、XML 文档。2 JAXB 如何工作JAXB 包括了一个运行类库和一个模式编译器。首先你要定义 XML 的 DTD,然后编写一个绑定模式(Binding Schema )。DTD 定义了 XML 文档,绑定模式也是一个 XML 文件,指出 DTD 定义的 XML 文档如何被映射为 Java 对象。运行编译器,将 DTD 和绑定模式作为参数传给编译器,编译器就会生成 Java 代码。编译生成的 Java 代码,通过这些代码就可以访问 XML 文档了。3 JAXB 的安装以 1.0 early access 为例,它不包含在 JDK 中,先到 http:/ 下载。注意由于是早期版本,需要先登录才能
5、下载,本文附带的源码包含了 JAXB1.0 early access。下载后将文件解压缩,在 lib 目录中有两个文件。 jaxb-rt-1.0-ea.jar 是运行支持库,jaxb-xjc-1.0-ea.jar 是模式编译器。注意 bin 目录中的 xjc 文件只能在UNIX 下使用,如果你的系统是 Windows,那么你需要在命令行窗口手工输入命令来编译。为了在任何地方都可以运行模式编译器和它生成的代码,我们要把这两的文件加入 CLASSPATH。一个简单的办法是把这两个文件拷贝到 jre/lib/ext 下。4 一个简单的例子有这样一个 XML 文档。它描述书的列表,举例如下:文件 ex
6、ampleA.xmlJava 编程入门张三2002-6-635.0XML 在 Java 中的应用 李四2002-9-1692.0其 DTD 文件如下:文件 bookList.dtd现在我们就来编写一个最简单的绑定模式,其文件扩展名应该为 xjs。文件 bookList.xjs现在就可以运行模式编译器生成 Java 代码,请先保证 CLASSPATH 中包含了JAXB 的两个 JAR 文件。 Windows 用户注意 bin 目录下的那个文件是没用的。在命令行运行:java com.sun.tools.xjc.Main bookList.dtd bookList.xjs如果没出问题,编译器就生成
7、了 Book.java,BookList.java 两个文件。你不用去理解这两个源文件里面的代码,只要知道怎么使用它们提供的方法就可以了。它们的继承结构都是这样的:java.lang.Objectjavax.xml.bind.ValidatableObjectjavax.xml.bind.MarshallableObjectjavax.xml.bind.MarshallableRootElementBookList or BookBookList.java 主要包含了以下方法BookList() /构造函数List getBook() /得到书的集合,List 中的对象实际类型是 Book,可
8、以添加、修改、删除其中的元素void deleteBook() /删除集合void emptyBook() /删除并生成一个新的空集合void marshal(X) /将数据写进 XML 文档void unmarshal(X) /将数据从 XML 文档读入对象void validate(X) /检查是否符合 DTD 约束,同时检查子树。在这个例子中就是BookList 的 Book 集合void validateThis() /检查是否符合 DTD 约束,不检查子树其中 marshal,unmarshal,validate 被重载,有多种参数形式(可以参考 JAXB 的API 文档)。Book
9、.java 主要包含了以下方法Book()String getName()String getAuthor()String getPublishDate()String getPrice()void setName(String x)void setAuthor(String x)void setPublishDate(String x)void setPrice(String x)void marshal()void unmarshal()void validate()现在我们就可以使用这两个文件访问 XML 了。首先编译这两个文件。编写一个 Test.java 文件,把它和生成的两个文件以
10、及前面的 exampleA.xml 放在一起。这个程序从 exampleA.xml 读入数据,作修改(把第一本书作者改成王五)后写入exampleB.xml。因为中文的编码问题,所以我们需要多一点手续。文件 Test.javaimport java.io.*;import java.util.*;import javax.xml.bind.*;import javax.xml.marshal.*;public class Testpublic static void main(String args) throws ExceptionBookList bl = new BookList();F
11、ileInputStream fis = new FileInputStream(“exampleA.xml“);trybl = bl.unmarshal(fis);finallyfis.close();List books = bl.getBook();Book b = (Book)books.get(0);b.setAuthor(“王五“);bl.validate(); /先验证,不然 marshal 会出错FileOutputStream fos = new FileOutputStream(“exampleB.xml“);XMLWriter xw = new XMLWriter(fos
12、,“GBK“);trybl.marshal(xw);finallyfos.close();编译运行,生成的文件 exampleB.xml 如下:Java 编程入门王五2002-6-635.0XML 在 Java 中的应用 李四2002-9-1692.05 更进一步:数据类型转换你可能已经注意到在上面的例子中,生成的 Book 对象的 getPrice 方法返回的是 String,实际上它应该是 float。同样 publishDate 以该是日期类型,而不是字符串。这是因为我们的绑定模式写得太简单了,模式编译器生成了默认的 String 类型。现在我们这样写:文件 bookList2.xjs用
13、 java com.sun.tools.xjc.Main bookList.dtd bookList2.xjs 运行编译器。生成的 Book文件的相应代码为:float getPrice()java.util.Date getPublishDate()bookList2.xjs 第 3 行将 Price 转换成了 float 类型,float 类型是一个简单类型,因此用 convert=“float“描述就可以了。而 publishDate 需要转变成 java.util.Date,这是一个类,而且他没有以字符串作为参数的构造函数。parse=“TransDate.parseDate“就表示使
14、用 unmarshal 读取数据的时候,会调用 TransDate.parseDate()方法。这个静态方法以字符串为参数,返回 java.util.date。print=“TransDate.printDate“的作用相反。TransDate 这个类需要我们提供。文件 TransDate.javaimport java.util.Date;public class TransDate private static java.text.SimpleDateFormat df = new java.text.SimpleDateFormat(“yyyy-MM-dd“);public static
15、 Date parseDate(String d) try return df.parse(d); catch (java.text.ParseException pe) System.out.print(pe);return new Date();public static String printDate(Date d) return df.format(d);6 那些使 JAXB 能够做到,但本文没有提到的本文提供的这个例子很简单,实际上 JAXB 还可以定义文档的哪些元素(属性)可以被转换成类,哪些被转换成类的属性。处理元素的属性。处理枚举值。为一些元素共同的子元素生成接口(因为 JA
16、XB 不支持 NameSpace),定义继承结构等等。7 JAXB 不能做到的Sun 的文档里提到的:仅支持用 DTD 定义 XML不支持 NameSpace不支持内部子集、NOTATIONs、ENTITY、ENTITIES 等。另外,我发现如果要写一条处理指令到 XML 文档中,例如指定转换的样式单在 JAXB 中好像做不到,在 javax.xml.marshal.XMLWriter 中有一个 chars(String str)方法,可以把字符串到 XML 文件的声明后面,但是这个方法对特殊字符作了转义,也就是没办法可以做到。这很奇怪,因为这是一个常用的功能,要实现也不难。也许还有我没有发现的办法。倒是有一个 doctype 方法可以写 DOCTYPE 声明。