Jaxb是JavaEE的規范.全稱Java Architecture for XML Binding.
可以根據XML Schema產生Java類的技術.JAXB也提供了將XML實例文檔反向生成Java對象樹的方法,並能將Java對象樹的內容重新寫到XML實例文檔.
JAXB 2.0是JDK 1.6的組成部分。JAXB 2.2.3是JDK 1.7的組成部分。在實際使用不需要引入新的jar.
我一般使用都是配合JPA使用,下面例子也是按JPA+JAXB來說明.
因此我需要引入jpa的實現包.hibernate-validator隨便.做驗證用的.
<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對象。
http://my.oschina.net/zhaoqian/blog/89763 這個是簡單的入門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);
}
實際案例:
<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示例.
<?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):
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
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並發處理:
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的 結構
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方法測試:
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的 結構
<?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>