【Java】JAXB操作XML用法详解

什么是JAXB?

JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。在JAX-WS(Java的WebService规范之一)中,JAXB可以实现对象和XML之间相互转换。Unmarshaller类管理将XML数据反序列化为新创建的Java内容树的进程,并可在解组时有选择的验证XML数据。Marshaller类负责管理将Java内容树序列化回XML数据的过程。

JAXB操作XML用法详解

在JDK1.6时,JAXB 2.0是JDK 1.6的组成部分。JAXB 2.2.3是JDK 1.7的组成部分。

JDK中JAXB相关的重要Class和Interface

  1、JAXBContext类,是应用的入口,用于管理XML/Java绑定信息。

  2、Marshaller接口,将Java对象序列化为XML数据。

  3、Unmarshaller接口,将XML数据反序列化为Java对象。

JDK中JAXB相关的重要Annotation注解

1、@XmlRootElement

说明:将类或枚举类型映射到XML元素,这是xml的入口,根节点,要记得在声明

范围:顶层类,枚举类型

属性说明:name: XML元素的本地名称,namespace: XML元素的名称空间名

2、@XmlElement

说明:将JavaBean属性映射到派生于属性节点名称的XML元素

范围:JavaBean属性、非static(静态)、非transient(瞬时)字段、XmlElements中的程序元素

属性说明:

  • defaultValue:此元素的默认值,

  • name:xml模式元素的名称,

  • namespace:xml模式元素的xml目标名称空间,

  • nillable:是否可以为空,默认false,

  • required:如果 required() 为 true,则将 Javabean 属性映射到一个 minOccurs="1" 的 XML 模式元素声明。maxOccurs 为 "1" 表示单个赋值的属性,maxOccurs 为 "unbounded" 则表示多个赋值的属性;如果 required() 为 false,则将 Javabean 属性映射到一个 minOccurs="0" 的 XML 模式元素声明。

注意:

如果只有属性,没有get/set方法,xml是不会输出该节点的,需要在属性上设置@XmlElement,如果有get方法要要在get上设置@XmlElement,不然会异常。

3、@XmlElements

说明:多个@XmlElement注解的容器,此注解用于注释JavaBean集合属性(如列表)

属性说明:value: @XmlElement注解集合

例如

@XmlElementWrapper(name="DEGREES")  
@XmlElements({
    @XmlElement(name="Degree",type=Degrees.class),  
    @XmlElement(name="b",type=String.class)  
})  
private List<Degrees> degrees;

4、@XmlAttribute

说明:将JavaBean属性映射到XML属性

范围:JavaBean属性、字段

5、@XmlAccessorType  

定义映射这个类中的何种类型需要映射到XML。可接收四个参数,分别是: 

  • XmlAccessType.FIELD:映射这个类中的所有字段到XML,不需要get/set方法,否则会报错

  • XmlAccessType.PROPERTY:映射这个类中的属性(get/set方法)到XML,如果没有初始值 或 没有get/set方法,xml不会输出

  • XmlAccessType.PUBLIC_MEMBER:将这个类中的所有public的field或property同时映射到XML(默认) ,只输出public修饰符的属性,或者是其他修饰符,但必须有 get/set方法

  • XmlAccessType.NONE:不映射,不输出任何字段内容

6、@XmlTransient  

定义某一字段或属性不需要被映射为XML。如当一个类的XmlAccessorType 被标注为PROPERTY时,在某一get/set方法的字段上标注此注解,那么该属性则不会被映射。   

注意:

测试发现只能设置单独在某一属性出现,不能和其他注解同时存在,否则会发生异常

7、@XmlType  

定义映射的一些相关规则  

  • propOrder:指定映射XML时的节点顺序,默认是随机的顺序           

  • factoryClass:指定UnMarshal时生成映射类实例所需的工厂类,默认为这个类本身

  • factoryMethod:指定工厂类的工厂方法

  • name:定义XML Schema中type的名称  (对应的xsd文件中的type)

  • namespace:指定Schema中的命名空间  

8、@XmlAccessorOrder

控制JAXB 绑定类中属性和字段的排序。其value属性,它有两个属性值:

  • XmlAccessOrder.ALPHABETICAL:对生成的xml元素按字母书序排序,满足基本需求这样就不需要使用propOrder一个个字母排序了

  • XmlAccessOrder.UNDEFINED:不排序

9、@XmlElementWrapper  

为数组元素或集合元素定义一个父节点。

如,类中有一元素为List items,若不加此注解,该元素将被映射为  

<item>...</item>  
<item>...</item>

设置完@XmlElementWrapper 后会生成这样的XML样式:

<items> 
    <item>...</item>  
    <item>...</item>  
</items>

10、@XmlJavaTypeAdapter

使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML。

比如下面例子对日期格式进行特殊的处理,需要继承xmladapter,从date类型转成string类型

@XmlJavaTypeAdapter(value=JaxbAdapter.class)  
private Date date;

...

public class JaxbAdapter extends XmlAdapter<String, Date> {  
    @Override  
    public Date unmarshal(String v) throws Exception {  
        return null;  
    }  
  
    @Override  
    public String marshal(Date v) throws Exception {  
        SimpleDateFormat simple = new SimpleDateFormat("yyyy/MM/dd");  
        return simple.format(v);  
    }  
}

排序

默认情况下,Jaxb编组出来的xml中的字段顺序是随机的,你可以使用@XmlType的propOrder属性来指定序列化的顺序。
第一步:定义java类时,使用@XmlType(propOrder = { "id", "name", "age","book"})指定输出顺序。

QQ截图20160811182656.jpg

注意:
    1.对于@XmlElementWrapper标注的属性,不能出现在@XmlType的propOrder列表中。

    2.对于所有@XmlElement标注过的属性,必须出现在@XmlType的propOrder列表中。

    3.此类可与以下注释一起使用: XmlRootElementXmlAccessorOrderXmlAccessorType、 XmlEnum。但是,当此注释用于枚举类型时,将忽略 XmlAccessorOrder 和 XmlAccessorType

测试源码

import java.io.StringReader;
import java.io.StringWriter;
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 javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;

import net.sf.json.JSONObject;

public class XMLTest3 {
    @XmlRootElement(name = "xml")
    @XmlAccessorType(XmlAccessType.PROPERTY)
    @XmlType
    static class A{
        private B b;
        private C c;
        
        private List<D> d;
        
        @XmlElement(name = "B")
        public B getB() {
            return b;
        }
        public void setB(B b) {
            this.b = b;
        }
        public C getC() {
            return c;
        }
        public void setC(C c) {
            this.c = c;
        }
        // 为数组元素或集合元素定义一个父节点
        @XmlElementWrapper(name="Dlist")
        @XmlElement(name="d")
        public List<D> getD() {
            return d;
        }
        public void setD(List<D> d) {
            this.d = d;
        }
    }
    
    static class B {
        private String b1;
        private String b2;
        
        @XmlAttribute
        public String getB1() {
            return b1;
        }
        public void setB1(String b1) {
            this.b1 = b1;
        }
        @XmlValue
        public String getB2() {
            return b2;
        }
        public void setB2(String b2) {
            this.b2 = b2;
        }
    }
    
    static class C {
        private String c1;
        private String c2="";  //想要将空值的xml节点输出来,请将该属性初始化一个空值
        
        public String getC1() {
            return c1;
        }
        public void setC1(String c1) {
            this.c1 = c1;
        }
        public String getC2() {
            return c2;
        }
        public void setC2(String c2) {
            this.c2 = c2;
        }
    }
    
    static class D {
        private String d1;
        private String d2;
        D(){}
        D(String d1,String d2){
            this.d1 = d1;
            this.d2 = d2;
        }
        public String getD1() {
            return d1;
        }
        public void setD1(String d1) {
            this.d1 = d1;
        }
        public String getD2() {
            return d2;
        }
        public void setD2(String d2) {
            this.d2 = d2;
        }
    }

    public static void main(String[] args) throws JAXBException {
        A a = new A();
        
        B b = new B();
        b.setB1("B1");
        b.setB2("B2");
        a.setB(b);
        
        C c = new C();
        c.setC1("C1");
        a.setC(c);
        
        List<D> list = new ArrayList<>();
        list.add(new D("d1","d2"));
        list.add(new D("d1","d2"));
        a.setD(list);
        
        // 构造报文 XML 字符串
        String xmlStr = object2Xml(a);
        System.out.println(xmlStr);
        
        // XML字符串转JavaBean对象
        A a2 = xml2Object(xmlStr, A.class);
        System.out.println(a.getB().getB1());
        
        // 不能使用JSONObject, 会有找不到内部类get/set方法异常,解决方法是将内部类提出来
//      System.out.println(JSONObject.fromObject(a2));  
    }
    
    /**
     * @param object 对象
     * @return 返回xmlStr
     */
    public static String object2Xml(Object object) {
        try {
            StringWriter writer = new StringWriter();
            JAXBContext context = JAXBContext.newInstance(object.getClass());
            Marshaller marshal = context.createMarshaller();

            marshal.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 格式化输出
            marshal.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");// 编码格式,默认为utf-8
            marshal.setProperty(Marshaller.JAXB_FRAGMENT, false);// 是否省略xml头信息
            marshal.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            marshal.marshal(object, writer);
            
            return new String(writer.getBuffer());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
     /**
     * @param xmlStr 字符串
     * @param c 对象Class类型
     * @return 对象实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T xml2Object(String xmlStr,Class<T> c) { 
        try { 
            JAXBContext context = JAXBContext.newInstance(c); 
            Unmarshaller unmarshaller = context.createUnmarshaller(); 
            return (T) unmarshaller.unmarshal(new StringReader(xmlStr)); 
        } catch (JAXBException e) {  e.printStackTrace();  return null; } 
    } 
}

结果
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xml>
    <B b1="B1">B2</B>
    <c>
        <c1>C1</c1>
        <c2></c2>
    </c>
    <Dlist>
        <d>
            <d1>d1</d1>
            <d2>d2</d2>
        </d>
        <d>
            <d1>d1</d1>
            <d2>d2</d2>
        </d>
    </Dlist>
</xml>

B1

常见异常

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
If a class has @XmlElement property, it cannot have @XmlValue property.
  this problem is related to the following location:
    at public java.lang.String com.chinatelecom.web.trade.demo.XMLTest3$B.getB2()
    at com.chinatelecom.web.trade.demo.XMLTest3$B
    at public com.chinatelecom.web.trade.demo.XMLTest3$B com.chinatelecom.web.trade.demo.XMLTest3$A.getB()
    at com.chinatelecom.web.trade.demo.XMLTest3$A

异常解读

遇到此类问题其实很好理解,一个类中是不允许 @XmlElement 和 @XmlValue两个注解同时存在的,当然也不允许有多个@XmlValue,你肯定没见过生成的格式是下面这种情况,虽然这两个不允许同时存在,但是可以多个@XmlAttribute和一个@XmlValue

 <B b1="B1">
     <b3>B3</b3>
     B2
 </B>


Jaxb下载:http://jaxb.java.net 

Jaxb官方手册:https://jaxb.java.net/2.2.11/docs/ch03.html


XmlType API:http://www.cjsdn.net/Doc/JDK60/javax/xml/bind/annotation/XmlType.html 

Marshaller API:http://www.apihome.cn/api/java/Marshaller.html

XmlAccessType API:

http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/javax/xml/bind/annotation/XmlAccessType.html

相关阅读:json和xml操作

【Java】Json-lib操作JSON示例

Java】Jackson 操作JSON教程示例

【Java】XStream 操作XML用法总结

【Java】JAXB操作XML用法详解


未经允许请勿转载:程序喵 » 【Java】JAXB操作XML用法详解

点  赞 (2) 打  赏
分享到: