JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。简介
JAXB 2.0是JDK 1.6的组成部分。JAXB 2.2.3是JDK 1.7的组成部分。
JAXBContext类,是应用的入口,用于管理XML/Java绑定信息。
Marshaller接口,将Java对象序列化为XML数据。
Unmarshaller接口,将XML数据反序列化为Java对象。
@XmlType,将Java类或枚举类型映射到XML模式类型
@XmlAccessorType(XmlAccessType.FIELD) ,控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标注)字段到XML。其他值还有XmlAccessType.PROPERTY和XmlAccessType.NONE。
@XmlAccessorOrder,控制JAXB 绑定类中属性和字段的排序。
@XmlJavaTypeAdapter,使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML。
@XmlElementWrapper ,对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。
@XmlRootElement,将Java类或枚举类型映射到XML元素。
@XmlElement,将Java类的一个属性映射到与属性同名的一个XML元素。
@XmlAttribute,将Java类的一个属性映射到与属性同名的一个XML属性。
其他
对于要序列化(marshal)为XML的Java类,绝不能把成员变量声明为public,否则运行将抛出异常com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException。
对于JAXB相关的重要Annotation的声明,如@Xml.....,可以放在成员变量的setter()或getter()方法上,两者中任选其一即可,但决不能放在成员变量上,否则运行将抛出异常com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException。
使用
"xjc"工具可以用来将XML模式或其他类型模式文件(Java 1.6试验性地支持,以及WSDL)转换为Java类。Java类使用javax.xml.bind.annotation包下的Java 标注,例如@XmlRootElement和@XmlElement。XML列表序列表示为java.util.List类型的属性, 通过JAXBContext可以创建Marshallers(将Java对象转换成XML)和Unmarshallers(将XML解析为Java对象)。
此外,JAXB包括了一个"schemagen"工具,能够执行"xjc"的反向操作,通过一组标注的Java类创建一个XML模式。
类型
下面的表格列出了JAXB中XML数据类型和Java数据类型的映射。
XML Schema类型 | Java数据类型 |
xsd:string | java.lang.String |
xsd:positiveInteger | java.math.BigInteger |
xsd:int | int |
xsd:long | long |
xsd:short | short |
xsd:decimal | java.math.BigDecimal |
xsd:float | float |
xsd:double | double |
xsd:boolean | boolean |
xsd:byte | byte |
xsd:QName | javax.xml.namespace.QName |
xsd:dateTime | javax.xml.datatype.XMLGregorianCalendar |
xsd:base64Binary | byte[] |
xsd:hexBinary | byte[] |
xsd:unsignedInt | long |
xsd:unsignedShort | int |
xsd:unsignedByte | short |
xsd:time | javax.xml.datatype.XMLGregorianCalendar |
xsd:date | javax.xml.datatype.XMLGregorianCalendar |
xsd:g | javax.xml.datatype.XMLGregorianCalendar |
xsd:anySimpleType | java.lang.Object |
xsd:anySimpleType | java.lang.String |
xsd:duration | javax.xml.datatype.Duration |
xsd:NOTATION | javax.xml.namespace.QName |
下面按JPA+JAXB来例子说明.
因此引入jpa的实现包.hibernate-validator随便.做验证用的.
1 2 3 4 5 6 7 8 9 10 | <dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa- 2.0 -api</artifactId>
<version> 1.0 . 1 .Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version> 5.0 . 1 .Final</version>
</dependency>
|
1. JDK中JAXB相关的重要Class和Interface:
JAXBContext类,是应用的入口,用于管理XML/Java绑定信息。
Marshaller接口,将Java对象序列化为XML数据。
Unmarshaller接口,将XML数据反序列化为Java对象。
这个是简单的入门demo.可以先运行试试,对JAXB有个大概的使用方法.下面例子将是系统正常做的.并对并发性进行处理的一个例子.
2. 常用注解说明
常用的annotation有:
@XmlType@XmlElement@XmlRootElement@XmlAttribute@XmlAccessorType@XmlAccessorOrder@XmlTransient@XmlJavaTypeAdapter@Temporal(TemporalType.XXXX) -->JPA中的时间处理注解,非JAXB@XmlElementWrapper 1.@XmlType
@XmlType用在class类的注解,常与@XmlRootElement,@XmlAccessorType一起使用。它有三个属性:name、propOrder、namespace,经常使用的只有前两个属性。如:
同时使用了@XmlType(propOrder={})和@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)的时候,生成的xml只按照propOrder定义的顺序生成元素
@XmlType(name = "basicStruct", propOrder = { "intValue", "stringArray", "stringValue")在使用@XmlType的propOrder 属性时,必须列出JavaBean对象中的所有属性,否则会报错。
2.@XmlRootElement
@XmlRootElement用于类级别的注解,对应xml的跟元素,常与 @XmlType 和 @XmlAccessorType一起使用。如:
@XmlType @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement public class Address {}3.@XmlElement
@XmlElement将java对象的属性映射为xml的节点,在使用@XmlElement时,可通过name属性改变java对象属性在xml中显示的名称。如:
@XmlElement(name="Address") private String yourAddress;4.@XmlAttribute
@XmlAttribute用于把java对象的属性映射为xml的属性,并可通过name属性为生成的xml属性指定别名。如:
@XmlAttribute(name="Country") private String state; 5.@XmlAccessorType
@XmlAccessorType用于指定由java对象生成xml文件时对java对象属性的访问方式。常与@XmlRootElement、@XmlType一起使用。它的属性值是XmlAccessType的4个枚举值,分别为: XmlAccessType.FIELD:java对象中的所有成员变量
XmlAccessType.PROPERTY:java对象中所有通过getter/setter方式访问的成员变量
XmlAccessType.PUBLIC_MEMBER:java对象中所有的public访问权限的成员变量和通过getter/setter方式访问的成员变量
XmlAccessType.NONE:java对象的所有属性都不映射为xml的元素
注意:@XmlAccessorType的默认访问级别是XmlAccessType.PUBLIC_MEMBER,因此,如果java对象中的private成员变量设置了public权限的getter/setter方法,就不要在private变量上使用@XmlElement和@XmlAttribute注解,否则在由java对象生成xml时会报同一个属性在java类里存在两次的错误。同理,如果@XmlAccessorType的访问权限为XmlAccessType.NONE,如果在java的成员变量上使用了@XmlElement或@XmlAttribute注解,这些成员变量依然可以映射到xml文件。
注意:虽然@XmlAccessorType为XmlAccessType.NONE,但是在java类的私有属性上加了@XmlAttribute和@XmlElement注解后,这些私有成员会映射生成xml的元素
6.@XmlAccessorOrder
@XmlAccessorOrder用于对java对象生成的xml元素进行排序。它有两个属性值:
AccessorOrder.ALPHABETICAL:对生成的xml元素按字母书序排序
XmlAccessOrder.UNDEFINED:不排序
7.@XmlTransient @XmlTransient用于标示在由java对象映射xml时,忽略此属性。即,在生成的xml文件中不出现此元素。8.@XmlJavaTypeAdapter @XmlJavaTypeAdapter常用在转换比较复杂的对象时,如map类型或者格式化日期等。使用此注解时,需要自己写一个adapter类继承XmlAdapter抽象类,并实现里面的方法。
@XmlJavaTypeAdapter(value=xxx.class),value为自己定义的adapter类
XmlAdapter 抽象接口如下:
public abstract class XmlAdapter<ValueType,BoundType> { // Do-nothing constructor for the derived classes.
protected XmlAdapter() {} // Convert a value type to a bound type. public abstract BoundType unmarshal(ValueType v); // Convert a bound type to a value type. public abstract ValueType marshal(BoundType v); } 实际案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <i> package jaxb.shop;
import java.util.Date;
import java.text.SimpleDateFormat;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DateAdapter extends XmlAdapter<String, Date> {
private String pattern = "yyyy-MM-dd HH:mm:ss" ;
SimpleDateFormat fmt = new SimpleDateFormat(pattern);
@Override
public Date unmarshal(String dateStr) throws Exception {
return fmt.parse(dateStr);
}
@Override
public String marshal(Date date) throws Exception {
return fmt.format(date);
}
}
//用于格式化日期在xml中的显示格式,并且由xml unmarshal为java对象时,将字符串解析为Date对象</i>
|
在某个类中如下使用,解析出对应的时间格式.必须重载那2个方法,用于JAXB marshal xml,xml unmarshal object时候使用.
@XmlJavaTypeAdapter(value=DateAdapter.class)
private Date purDate; 9.但如果是和JPA一起使用的话,可以使用@Temporal(TemporalType.DATE)来格式时间,默认为TemporalType.TIMESTAMP类型.TemporalType属性如下:
public enum TemporalType { DATE, //java.sql.Date TIME, //java.sql.Time TIMESTAMP //java.sql.Timestamp}java.sql.Date
日期型,精确到年月日,例如“2008-08-08”
java.sql.Time
时间型,精确到时分秒,例如“20:00:00”
java.sql.Timestamp
时间戳,精确到纳秒,例如“2008-08-08 20:00:00.000000001”
10.在JAXB标准中,@XmlElementWrapper注解表示生成一个包装 XML 表示形式的包装器元素。 此元素主要用于生成一个包装集合的包装器 XML 元素。
注:@XmlElementWrapper仅允许出现在集合属性上。最后的案例将使用这个注解.
3. 最终案例(模拟XML--系统 --DB)
例子XML示例.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <? xml version = "1.0" encoding = "utf-8" ?>
< userinfo xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd = "http://www.w3.org/2001/XMLSchema" >
< id >110</ id >
< name >Credo</ name >
< address >China BeiJing</ address >
< job >programmer</ job >
< overinfos >
< overinfo >
< hobby >Accompany my girlfriend.</ hobby >
<!--开始日期 dateTime-->
< beginDate >2009-06-02T12:00:00</ beginDate >
<!--结束日期 dateTime-->
< endDate >2109-06-02T12:00:00</ endDate >
</ overinfo >
< overinfo >
< hobby >Write some code.</ hobby >
<!--开始日期 dateTime-->
< beginDate >2009-06-02T12:00:00</ beginDate >
<!--结束日期 dateTime-->
< endDate >2029-06-02T12:00:00</ endDate >
</ overinfo >
</ overinfos >
</ userinfo >
|
Model层(JAXB+JPA):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | package org.credo.jaxb.model;
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import org.hibernate.validator.constraints.Length;
/**
* @author Credo
*/
@XmlRootElement
@XmlAccessorType (XmlAccessType.FIELD)
@Entity
@Table (name = "USERINFO" )
public class Userinfo implements Serializable{
private static final long serialVersionUID = 7870351249722416047L;
@Id
@Column (name = "ID" , nullable = false )
private Long id;
@Column (name = "NAME" , length = 50 )
@Length (max = 50 )
private String name;
@Column (name = "ADDRESS" , length = 50 )
@Length (max = 50 )
private String address;
@Column (name = "JOB" , length = 50 )
@Length (max = 50 )
private String job;
@XmlElementWrapper (name = "overinfos" )
@OneToMany (cascade = CascadeType.ALL)
@XmlElements (value = { @XmlElement (name = "overinfo" , type = Overinfo. class ) })
private List<Overinfo> overinfos;
public Long getId() {
return id;
}
public void setId(Long id) {
this .id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this .address = address;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this .job = job;
}
public List<Overinfo> getOverinfos() {
return overinfos;
}
public void setOverinfos(List<Overinfo> overinfos) {
this .overinfos = overinfos;
}
}
|
Overinfo.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | package org.credo.jaxb.model;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType (XmlAccessType.FIELD)
@XmlType (name = "overinfo" )
@Entity
@Table (name = "OVERINFO" )
public class Overinfo implements Serializable {
private static final long serialVersionUID = 2579971237985854291L;
@XmlTransient
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
@Column (name = "ID" )
private Long id;
@XmlTransient
@ManyToOne (cascade = CascadeType.ALL)
@JoinColumn (name = "UserinfoId" )
private Userinfo userinfo;
@Column (name = "hobby" , length = 20 )
private String hobby;
@Temporal (TemporalType.DATE)
@Column (name = "beginDate" , length = 20 )
private Date beginDate;
@Temporal (TemporalType.DATE)
@Column (name = "endDate" , length = 20 )
private Date endDate;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this .hobby = hobby;
}
public Date getBeginDate() {
return beginDate;
}
public void setBeginDate(Date beginDate) {
this .beginDate = beginDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this .endDate = endDate;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this .id = id;
}
public Userinfo getUserinfo() {
return userinfo;
}
public void setUserinfo(Userinfo userinfo) {
this .userinfo = userinfo;
}
}
|
JAXB并发处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package org.credo.jaxb;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
public final class JAXBCache {
private static final JAXBCache instance = new JAXBCache();
private final ConcurrentMap<String, JAXBContext> contextCache = new ConcurrentHashMap<String, JAXBContext>();
private JAXBCache() {
}
public static JAXBCache instance() {
return instance;
}
JAXBContext getJAXBContext(Class<?> clazz) throws JAXBException {
JAXBContext context = contextCache.get(clazz.getName());
if ( context == null )
{
context = JAXBContext.newInstance(clazz);
contextCache.putIfAbsent(clazz.getName(), context);
}
return context;
}
}
|
JAXBExportSchema 导出JAXB的 class的 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package org.credo.jaxb;
import java.io.File;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import org.credo.jaxb.model.Userinfo;
/**
* JAXB 导出Schema。
*
* @author: Credo
* @date: 2013-6-25
*/
public class JAXBExportSchema {
public static void main(String[] args) {
JAXBContext jct;
try
{
jct = JAXBContext.newInstance(Userinfo. class );
jct.generateSchema( new Resolver());
}
catch ( Exception ex )
{
ex.printStackTrace();
}
}
}
class Resolver extends SchemaOutputResolver {
@Override
public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
File file = new File( "d:\\" , suggestedFileName);
StreamResult result = new StreamResult(file);
result.setSystemId(file.toURI().toURL().toString());
return result;
}
}
|
JAXBUtil以及main方法测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | package org.credo.jaxb;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.apache.commons.io.IOUtils;
import org.credo.jaxb.model.Overinfo;
import org.credo.jaxb.model.Userinfo;
/**
* marshal对象和unmarshal对象都是由JAXBContext创建.所以一开始需要初始化JAXBContext.
* @author Credo
*/
public class JAXBUtil {
/**
* 生成xml文件的二进制数据
* @param obj 对象
*/
public static byte [] marshal(Object obj) throws JAXBException {
JAXBContext context = JAXBCache.instance().getJAXBContext(obj.getClass());
Marshaller m = context.createMarshaller();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(obj, outputStream);
byte [] result = outputStream.toByteArray();
return result;
}
/**
* @param data xml stream
* @param classe 类
* @return jaxb生成xml的java 类对象
*/
public static Object unmarshal( byte [] data, Class<?> classe) throws JAXBException {
JAXBContext context = JAXBCache.instance().getJAXBContext(classe);
Unmarshaller m = context.createUnmarshaller();
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
Object obj = m.unmarshal(inputStream);
return obj;
}
/**
* @param data xml stream
* @param classe 类
* @return jaxb生成xml的java 类对象
*/
public static Object unmarshal(InputStream in, Class<?> classe) throws JAXBException, IOException {
JAXBContext context = JAXBCache.instance().getJAXBContext(classe);
byte [] data = IOUtils.toByteArray(in);
Unmarshaller m = context.createUnmarshaller();
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
Object obj = m.unmarshal(inputStream);
return obj;
}
public static void main(String[] args) throws JAXBException {
Userinfo userinfo = new Userinfo();
userinfo.setId(Long.valueOf( 11 ));
List<Overinfo> list = new ArrayList<Overinfo>();
Overinfo e = new Overinfo();
e.setHobby( "陪女友" );
list.add(e);
Overinfo e1 = new Overinfo();
e1.setHobby( "写代码" );
list.add(e1);
userinfo.setOverinfos(list);
byte [] b = JAXBUtil.marshal(userinfo);
System.out.println( new String(b));
userinfo = (Userinfo) JAXBUtil.unmarshal(b, Userinfo. class );
System.out.println(userinfo.getOverinfos().get( 0 ).getHobby());
}
}
|
就不说明了,仔细看代码的,一会就明白了.不看的运行下也明白了.下面是上面main方法测试的输出结果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<userinfo> <id>11</id> <overinfos> <overinfo> <hobby>陪女友</hobby> </overinfo> <overinfo> <hobby>写代码</hobby> </overinfo> </overinfos></userinfo>下面是使用JAXBExportSchema 导出JAXB的 class的 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?xml version= "1.0" encoding= "UTF-8" standalone= "yes" ?>
<xs:schema version= "1.0" xmlns:xs= "http://www.w3.org/2001/XMLSchema" >
<xs:element name= "userinfo" type= "userinfo" />
<xs:complexType name= "userinfo" >
<xs:sequence>
<xs:element name= "id" type= "xs:long" minOccurs= "0" />
<xs:element name= "name" type= "xs:string" minOccurs= "0" />
<xs:element name= "address" type= "xs:string" minOccurs= "0" />
<xs:element name= "job" type= "xs:string" minOccurs= "0" />
<xs:element name= "overinfos" minOccurs= "0" >
<xs:complexType>
<xs:sequence>
<xs:element name= "overinfo" type= "overinfo" minOccurs= "0" maxOccurs= "unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name= "overinfo" >
<xs:sequence>
<xs:element name= "hobby" type= "xs:string" minOccurs= "0" />
<xs:element name= "beginDate" type= "xs:dateTime" minOccurs= "0" />
<xs:element name= "endDate" type= "xs:dateTime" minOccurs= "0" />
</xs:sequence>
</xs:complexType>
</xs:schema>
|