@XmlRootElement 注解用于标注类或枚举类型,用它标注的类在映射后的 schema 中会以一个全局元素的形式出现,元素的类型是一个包含 Java 类属性的 XML 复杂数据类型。我们可以通过 @XmlRootElement 注解的 name 属性来定制映射的 schema 全局元素的名称,一般来说以 @XmlRootElement 标注的类在相应的 XML 文档中会以最外层或根节点形式出现。比方说有如下标注类:
@XmlRootElement(name="RootStudent",namespace="http://service.dsw.ibm.com/") public class Student { ... } 这个 Java 类在映射后的 Web 服务 schema 中会表现为: <xs:element name="RootStudent" type="tns:student"/> <xs:complexType name="student"> <xs:sequence> <xs:element name="id" type="xs:int"/> <xs:element name="name" type="xs:string"/> </xs:sequence> </xs:complexType> |
@XmlType 注解用于标注类或枚举类型,用它标注的类在映射后的 schema 中中会以一个 XML 复杂数据类型的形式出现。我们可以通过 @XmlType 注解的 name 属性来定制映射的 XML 数据类型的名称,用 propOrder 属性来定制映射后的复杂数据类型的内容顺序等。比方说有如下标注类:
@XmlType(name = "CustomizedStudent", propOrder = { "name", "id", "age" }) public class Student { private int id; private int age; private String name; .... } |
这个 Java 类在映射后的 Web 服务 schema 中会表现为:
<xs:complexType name="CustomizedStudent"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="id" type="xs:int"/> <xs:element name="age" type="xs:int"/> </xs:sequence> </xs:complexType> |
@XmlElement 注解用于标注 Javabean 的属性,用它标注的属性在映射后的 schema 中以元素的形式出现,所有 Javabean 属性映射的元素组合成为 @XmlType 映射的复杂数据类型。我们可以通过 @XmlElement 注解的 name 属性定制映射后的 XML 元素的名称,用 required 属性来指定该元素是否必须出现,用 nillable 属性来指明该元素是否允许空值。比方说有如下标注类:
@XmlType(name = "CustomizedStudent", propOrder = { "name", "id", "age"}) public class Student { @XmlElement(name="StudentID",required=true) private int id; @XmlElement(name="StudentAge",nillable=true) private int age; @XmlElement(name="StudentName") private String name; } |
这个 Java 类在映射后的 Web 服务 schema 中会表现为:
<xs:complexType name="CustomizedStudent"> <xs:sequence> <xs:element minOccurs="0" name="StudentName" type="xs:string"/> <xs:element name="StudentID" type="xs:int"/> <xs:element name="StudentAge" nillable="true" type="xs:int"/> </xs:sequence> </xs:complexType> |
@XmlAttribute 注解用于标注 Javabean 属性,用它标注的属性在映射后的 schema 中以元素属性形式表现。我们可以通过 @XmlAttribute 注解的 name 属性来定制映射后的名称,用 required 属性来指定是否映射后的属性为必须出现的。比方说有如下标注类:
@XmlType(name = "CustomizedStudent", propOrder={"name","id","age"}) public class Student { @XmlElement(name="StudentID") private int id; @XmlAttribute(name="StudentAge") private int age; @XmlAttribute(name="StudentName",required=true) private String name; } |
这个 Java 类在映射后的 Web 服务 schema 中会表现为:
<xs:complexType name="CustomizedStudent"> <xs:sequence> <xs:element name="StudentID" type="xs:int"/> </xs:sequence> <xs:attribute name="StudentName" type="xs:string" use="required"/> <xs:attribute name="StudentAge" type="xs:int" use="required"/> </xs:complexType> |
@XmlAccessorType
@XmlTransient
@XmlJavaTypeAdaptor
@XmlSeeAlso
我们的 XSD 使用了一个有意思的 XML 模式特性 —— 继承.文章 Polymorphic Web services 详细介绍了这个特性,解释了 XML 扩展和动态服务调用技术如何提供多态性.我们在 JAXB 的上下文中考虑一下图 10 所示的模式.
BaseNotification
分别派生出 TaxNotification
和 PaymentNotification
。它们继承基类型的所有属性和元素,并添加自己的新属性,见清单 8
见清单 8
- <xsd:complexType abstract="true" name="BaseNotification">
- …
- </xsd:complexType>
- <xsd:complexType name="TaxNotification">
- <xsd:complexContent>
- <xsd:extension base="bo:BaseNotification">
- </xsd:extension>
- </xsd:complexContent>
- </xsd:complexType>
- <xsd:complexType name="PaymentNotification">
- <xsd:complexContent>
- <xsd:extension base="bo:BaseNotification">
- ....
<xsd:complexType abstract="true" name="BaseNotification">
…
</xsd:complexType>
<xsd:complexType name="TaxNotification">
<xsd:complexContent>
<xsd:extension base="bo:BaseNotification">
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="PaymentNotification">
<xsd:complexContent>
<xsd:extension base="bo:BaseNotification">
....
JAXB 运行时通过内省注册所需的所有 Java 类.现在,如果在运行时作为基类型传递派生的子类,那么无法通过检查接口参数类型识别出子类.
为了利用继承和多态性,在 JAXB 2.1 和 Java 6.0 中引入了一个新注解 javax.xml.bind.annotation.XmlSeeAlso.@XmlSeeAlso 通过引用在 JAXBContext 中添加的额外派生子类类以及根据接口参数识别出的类实现多态性,见清单 9.
清单 9.
- @XmlSeeAlso({
- PaymentNotification.class,
- TaxNotification.class
- })
- public abstract class BaseNotification {
- ...
- }
@XmlSeeAlso({
PaymentNotification.class,
TaxNotification.class
})
public abstract class BaseNotification {
...
}
==========================================================================================================
一、前言。
最近正在做一个项目,根据需求计划需要一种 Object <--> XML的工具,而JAXB就非常符合需求,于是以下是近天来的研究心得。
二、JAXB技术介绍
1. 什么是JAXB?
Java Architecture for XML Binding (JAXB) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。
这意味着你不需要处理甚至不需要知道XML编程技巧就能在Java应用程序中利用平台核心XML数据的灵活性。而且,可以充分利用XML的优势而不用依赖于复杂的XML处理模型如SAX或DOM。JAXB 隐藏了细节并且取消了SAX和DOM中没用的关系——生成的JAXB类仅描述原始模型中定义的关系。其结果是结合了高度可移植Java代码和高度可移植的XML数据。其中这些代码可用来创建灵活、轻便的应用程序和Web服务。
2. JAXB的体系结构
JAXB的体系结构和应用过程如图所示,一般来说包含以下几个步骤:
1) 根据你的应用程序所要操作的XML数据格式,撰写相应的XML Schema,有关XML Schema的知识,请参阅“参考资料”
2) 使用JAXB 所带的编译工具(Binding Compiler),将这个XML Schema文件作为输入,产生一系列相关的Java Class和Interface
3) 在使用JAXB编译工具的时候,可以有选择性的提供一个配置文件(图的虚线部分),来控制JAXB编译工具的一些高级属性。
4) 这些Java Class和Interface是你的应用程序操纵XML数据的主要接口和方法。
5) 通过JAXB对XML文档进行的操作主要包括:将符合XML Schema规定的XML文档解析生成一组相应的Java对象;对这些对象进行操作(修改、增加和删除对象的属性等等);然后将这些对象的内容保存到这个XML文档中。
三、安装介绍
1. 开发环境准备
1) Java 1.5 JDK/JRE
(*) 你必须安装java运行环境
2) Eclipse SDK 3.2.x
Eclipse推荐下载的版本是3.2.1 。
3) My Eclipse 5.x (推荐使用)
能为你的XML编写提供帮助,与Eclipse SDK 3.2.x配套使用。
2. 开始安装:
1) 需要下载的软件
A. 下载开发工具包:Java Web Services Developer Pack 2.0
JAXB2.0的正式版本的一个实现(大家一定要记住,JAXB只是一个标准,Sun公司提供的此工具包只能说是这个标准的一个实现)已经发布了。由于XML在Web Services中的大量应用,所以,JAXB2.0作为Web Services 开发包的一部分,可以从SUN的官方网站下载。其中jaxb包含在jaxb子目录下。
B. XML文档解析:xerces-2_9_0
Xerces是由Apache组织所推动的一项XML文档解析开源项目,jwsdp提供的xerces似乎有Bug,建议下载最新版本,下载请到Apache的官方网站下载。
2) 环境变量配置
要使用JAXB,在下载JAXB开发包以后,还要设置一些环境变量,主要是设置classpath的路径,以提供JAXB包所带的库文件的位置。
JAVA_HOME=D:/java/jdk1.5.0 JWSDP_HOME=D:/Sun/jwsdp-2.0 JAXB_HOME=%JWSDP_HOME%/jaxb JAXB_LIBS=%JAXB_HOME%/lib JAXP_LIBS=%JWSDP_HOME%/jaxp/lib JWSDP_LIBS=%JWSDP_HOME%/jwsdp-shared/lib PATH=%JAXB_HOME%/bin;%JWSDP_HOME%/jwsdp-shared/bin;%PATH% CLASSPATH= %JWSDP_HOME%/jaxb/lib/jaxb-api.jar; %JWSDP_HOME%/jaxb/lib/jaxb-impl.jar; %JWSDP_HOME%/jaxb/lib/jaxb-xjc.jar; %JWSDP_HOME%/jwsdp-shared/lib/activation.jar; %JWSDP_HOME%/sjsxp/lib/jsr173_api.jar; %JWSDP_HOME%/sjsxp/lib/sjsxp.jar; |
注:注意自己原来的CLASSPATH设置,不要覆盖了。
3) Eclipse 工程构建路径配置:
注:如图所示的JAR都添加到工程的构建路径中。
四、实例分析
1. 确定XML Schema
要确定XML Schema意味着你要确定一个规则,来约束你的XML文档,使所有符合这个规则的XML文档看上去都很类似。例如,在这个例子中,我们希望XML文档的格式如下:
<?xml version="1.0" encoding="UTF-8"?> <users> <user> <name>小李</name> <sex>男</sex> <age>24</age> </user> <user> <name>小丽</name> <sex>女</sex> <age>18</age> </user> <user> <name>小王</name> <sex>男</sex> <age>22</age> </user> </users> |
XML文档所包含的意思:一个用户组 <users> 里面有多个 <user>,而每个 <user> 又包括<name><sex><age>这些信息,而要在应用程序中想要操纵此XML文档,还要根据这个XML文档,产生一个Schema文档。
<?xml version="1.0" encoding="UTF-8"?> <schema targetNamespace="us" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="us">
<simpleType name="Sex"> <restriction base="string"> <enumeration value="男"></enumeration> <enumeration value="女"></enumeration> </restriction> </simpleType>
<complexType name="User"> <sequence> <element name="name" type="string"></element> <element name="age" type="int"></element> <element name="sex" type="tns:Sex"></element> </sequence> </complexType>
<element name="user" type="tns:User"></element>
<complexType name="Users"> <sequence> <element ref="tns:user" minOccurs="1" maxOccurs="unbounded"> </element> </sequence> </complexType>
<element name="users" type="tns:Users"></element> </schema>
|
1) 它规定了<users>这个节点内可以包含一个或多个<user>的节点。
2) 每个<user>节点包括名字、年龄、性别这几个信息
3) 规定了<sex>属性由“男”和“女”组成
2. 使用编译工具生成相应的Java类
有了Schema文件以后,我们就可以利用JAXB工具包,让它来替我们生成操纵符合这个Schema规定的所有XML实例文档的所有Java源代码。
如果已经设置好了环境变量的话(请参考“设置环境变量”),那么只需要运行JAXB包所带的“xjc”运行程序,例如
%JAXB_HOME%/bin/xjc.bat userSchema.xsd -d src –p primer.userinfo
其中:
A. %JAXB_HOME%是你安装JAXB工具包的位置,通常在jwsdp工具包的子目录下。
B. userSchema.xsd 是Schema的文件名,一般以xsd作为文件名的后缀。
C. -d 的选项,是指定系统生成的Java源代码所放置的目录,示例中为:src
D. -p 的选项,是指定系统生成的Java源代码所在的Java Package的名称,示例中为:primer.userinfo
E. 还有更多的选项,请参考JAXB的相关文档。
如果运行成功的话,就会在你"-d"选项指定的目录下产生一些java代码文件:
3. 在应用程序中使用这些代码
下面我们分析一下如何在我们的应用程序中使用JAXB工具包替我们生成的代码。
1) JAXBContext 提供了一个入口,通过这个入口可以管理必要的XML/Java绑定信息。客户端应用程序通过newInstance(contextPath)方法得到该类的新实例。contextPath 参数包含一个或多个Java包名,这些Java包就是了JAXB编译器所产生的接口代码所在的Java包。该参数值初始化JAXBContext 对象,使得它能够管理JAXB编译器产生的接口。
JAXBContext jc = JAXBContext.newInstance("primer.userinfo");
|
2) 而通过ObjectFactory,可以直接生成根节点java对象Users 和子对象User。
User userA = objFactory.createUser(); |
Users users = objFactory.createUsers(); |
3) 对于Marshaller和Unmarshaller,这两个对象主要是用于Java对象和XML文档互相转换的主要接口(Marshaller负责从Java对象到XML文档的转换,Unmarshaller负责从XML文档到Java对象的转换)。
JAXBElement usersE = objFactory.createUsers(users); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal( usersE,new FileOutputStream("src/test.xml")); |
JAXBElement usersE = (JAXBElement)jc.createUnmarshaller().unmarshal(new File("src/test.xml")); Users users = (Users)usersE.getValue(); |
4. 实例代码
publicvoid javaToXml() { try { JAXBContext jc = JAXBContext.newInstance("primer.userinfo"); //参数为JAXB生成的java文件所在包名 ObjectFactory objFactory = new ObjectFactory(); //生成对象工厂
User userA = objFactory.createUser(); userA.setName("西瓜"); userA.setAge(26); userA.setSex(Sex.男);
User userB = objFactory.createUser(); userB.setName("冬瓜"); userB.setAge(19); userB.setSex(Sex.女);
User userC = objFactory.createUser(); userC.setName("南瓜"); userC.setAge(22); userC.setSex(Sex.男);
Users users = objFactory.createUsers(); List<User> list = users.getUser(); list.add(userA); list.add(userB); list.add(userC);
JAXBElement usersE = objFactory.createUsers(users); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal( usersE,new FileOutputStream("src/test.xml")); } catch (Exception e) { e.printStackTrace(); } } |
五、参考资料
1. JAXB
http://java.sun.com/webservices/jaxb/
2. XML Schema
http://www.w3.org/1999/05/06-xmlschema-1/
http://www.w3.org/1999/05/06-xmlschema-2/
JAXB(Java Architecture for XML Binding)是一种特殊的序列化/反序列化工具。它可以使XML数据以Java Objects的形式直接应用于Java程序之中,使Java Objects与XML数据之间的转换成为可能。在JAXB中将Java Objects到XML数据的转换称为marshal;XML数据到Java Objects的转换称为unmarshal。
如图所示,使用JAXB分两步,首先利用xjc命令行程序编译DTD或Schema文件生成与XML数据结构相对应的JavaBeans类,再通过JAXB API中的marshal与unmarshal操作完成JavaBeans对象与XML数据之间的互换。
JAXB使用了最新的元数据技术,故而只能在JDK 1.5以上版本使用,JAXB的官方网站如下:
https://jaxb.dev.java.net/
可以在其中下载到最新的JAXB。安装之后的JAXB目录结构如下:
/JAXB-HOME(path/to/jaxb)
/bin
/lib
/docs
在bin目录中有所有的JAXB工具,其中包括xjc命令行程序;在lib目录中可以找到所有JAXB的jar包;在docs目录中有一个JAXB的使用帮助及API说明。
一. xjc使用帮助
1. 通过命令行编译
命令行程序xjc用于编译DTD或Schema生成与XML数据结构对应的JavaBeans类。操作方法如下:
xjc [-xmlschema|-dtd] [–d 保存java文件的目录] [–p java文件所在的包] <编译文件>
-xmlschema 指定编译文件的格式为Schema格式,此参数是默认值,可以不用提供。
如果不提供此参数,编译文件必须是Schema文件。
-dtd 指定编译文件的格式为DTD格式,如果编译文件是DTD文件则必须提供此参数。
-d 指定了保存java文件的目录
-p 指定了生成的java文件所在的包
例如编译当前目录下的schema.xls文件,java文件保存在src目录,包名为generate,其命令如下:
xjc –d src –p generate schema.xsd
如果编译文件是schema.dta,则命令如下:
xjc –dtd –d src –p generate schema.dtd
2. 通过ANT编译
在/JAXB-HOME/lib目录中的jaxb-xjc.jar包中包含了XJCTask.class文件,可以依据该类定义一个ant task,通过执行该task生成java文件,task的定义如下:
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask">
<classpath>
<fileset dir="${JAXB-HOME}/lib" includes="*.jar" />
</classpath>
</taskdef>
执行xjc task的ant代码样例如下:
<target name="generate" description="generate Java source files">
<echo message="generate java from schema..." />
<mkdir dir="src" />
<xjc schema="books.xsd" package="com.chris.jaxb" destdir="gen-src">
<produces dir="src/com/chris/jaxb " includes="**/*.java" />
</xjc>
</target>
其中
schema 指定DTD或Schema文件的位置;
destdir 指定生成的java文件所保存的根目录;
package 指定生成的java文件所在的包;
args 指定额外的参数,其中最重要的参数是“-dtd”,如果schema指定的是一个DTD文件,则args中必须提供“-dtd”;
produces 用于在指定目录中检查最新版本。
二. 序列化(Marshalling)与反序列化(Unmarshalling)
要想在Java程序中使用JAXB,必须用到以下几个jar包:
jaxb-api.jar
jaxb-impl.jar
activation.jar
jsr173_1.0_api.jar
以上包均在/JAXB-HOME/lib目录中可以找到。
JAXB的核心类是JAXBContext类,该类可以根据一个包名或一个类名加载Java Objects。
使用包名加载Java Objects的样例如:
JAXBContext jc = JAXBContext.newInstance("generate");
其指定的包中必须包含ObjectFactory类,该类由xjc生成,用于生成XML数据所对应的Java Objects。
如果使用类名加载Java Object,例如:
JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
其指定的类必须是用于生成Java Objects的工厂类,如ObjectFactory类。
为了方便使用,下面的代码封装了JAXB相关类,样例以包名加载方式为主,提供了基本的marshal与unmarshal操作。
- package generated;
- import java.io.InputStream;
- import java.io.OutputStream;
- import javax.xml.bind.JAXBContext;
- import javax.xml.bind.JAXBException;
- import javax.xml.bind.Marshaller;
- import javax.xml.bind.Unmarshaller;
- public class JAXBUtils {
- private JAXBUtils() {
- throw new UnsupportedOperationException();
- }
- public static Object unmarshal(String contextPath, InputStream xmlStream)
- throws JAXBException {
- JAXBContext jc = JAXBContext.newInstance(contextPath);
- Unmarshaller u = jc.createUnmarshaller();
- return u.unmarshal(xmlStream);
- }
- public static void marshal(String contextPath, Object obj,
- OutputStream stream) throws JAXBException {
- JAXBContext jc = JAXBContext.newInstance(contextPath);
- Marshaller m = jc.createMarshaller();
- m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
- m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
- m.marshal(obj, stream);
- }
- }
使用这个工具类非常简单,代码如下:
- public class JaxbTest {
- public static void main(String[] args) throws FileNotFoundException,
- JAXBException {
- // unmarshal
- InputStream is = new FileInputStream("C:/test.xml");
- Object obj = JAXBUtils.unmarshal("generate", is);
- System.out.println(v.getUserid());
- // marshal
- OutputStream f = new FileOutputStream("C:/Test1.xml");
- JAXBUtils.marshal("generate", obj, f);
- }
- }