JAXB:java对象和xml之间转换

JAXB(Java Architecture for XML Binding)是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。

常用注解:

@XmlRootElement:将类映射为根元素

该注解含有name和namespace两个属性。namespace属性用于指定生成的元素所属的命名空间。name属性用于指定生成元素的名字,若不指定则默认使用类名小写作为元素名。

@XmlElement:将被注解的字段映射为子元素。

name属性可以指定生成元素的名字

@XmlAttribute:将字段映射成本类对应元素(标签)的属性

@XmlTransient:在映射xml元素时忽略被注解的字段

@XmlAccessorType:决定哪些字段会被映射为xml元素。

  XmlAccessType.FIELD:java对象中的所有成员变量

  XmlAccessType.PROPERTY:java对象中所有通过getter/setter方式访问的成员变量(属性)

  XmlAccessType.PUBLIC_MEMBER:java对象中所有的public访问权限的成员变量和通过getter/setter方式访问的成员变量

  XmlAccessType.NONE:java对象的所有属性都不映射为xml的元素

@XmlElementWrapper:用在集合对象上,映射后包装同一个元素。此时@XmlElement可以没有。

@XmlJavaTypeAdapter:指定自定义适配器,解决java日期(Date),数字(Number)格式化问题。

实例:

实体类

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)  # 表示成员变量就可以被转换成xml中的标签
public class Boy {
    public String name = "CY";
}

测试类

public class JAXBTest {
    public static void main(String[] args) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(Boy.class);

        Marshaller marshaller = context.createMarshaller();
        Unmarshaller unmarshaller = context.createUnmarshaller();
     // marshall将java对象转成xml
        Boy boy = new Boy();
        marshaller.marshal(boy, System.out);
        System.out.println();
     // unmarshall将xml转成java对象
        String xml = "<boy><name>David</name></boy>";
        Boy boy2 = (Boy) unmarshaller.unmarshal(new StringReader(xml));
        System.out.println(boy2.name);

    }
}

结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><boy><name>CY</name></boy>
David

改造一:

将XmlAccessType.FIELD改为XmlAccessType.PROPERTY

@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)  # 只有属性才能转换成xml中的标签
public class Boy {
    public String name = "CY";
}

再次运行,结果为:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><boy/>
CY

发现Marshall和unMarshall都失败。由于name没有getter/setter方法,故不是属性,所以java对象转成xml时,name不转为标签

该造二:

给name属性添加 get set 方法。

@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Boy {
    public String name = "CY";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

再次执行,结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><boy><name>CY</name></boy>
David

结果正常

改造三:

给Boy 再添加一个field, int age=10

再次运行,结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><boy><name>CY</name></boy>
David

显然,这个age 是不会被 转化 到xml 文件中的。解决办法是:给age添加@XmlElement注解

@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Boy {
    public String name = "CY";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @XmlElement
    int age=10;
}

再次运行,结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><boy><age>10</age><name>CY</name></boy>
David

发现多了一个age标签

使用@XmlElement注解,成员变量可以映射为标签,

@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Boy {
    @XmlElement
    public String name = "CY";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @XmlElement
    int age=10;
}

但是属性不能加@XmlElement注解,否则报错如下:

Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
类的两个属性具有相同名称 "name"
    this problem is related to the following location:
        at public java.lang.String com.ljxx.entity.business.Boy.getName()
        at com.ljxx.entity.business.Boy
    this problem is related to the following location:
        at public java.lang.String com.ljxx.entity.business.Boy.name
        at com.ljxx.entity.business.Boy

    at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:106)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:460)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:292)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:139)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1138)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:162)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:247)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:234)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:441)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:641)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
    at com.ljxx.JAXBTest.main(JAXBTest.java:18)

改造四:

将@XmlRootElement改为@XmlRootElement(name="b" nameSpace="http://test"),

@XmlRootElement(name="b",namespace ="http://test")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Boy {
    public String name = "CY";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @XmlElement
    int age=10;
}

测试类

public class JAXBTest {
    public static void main(String[] args) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(Boy.class);

        Marshaller marshaller = context.createMarshaller();
        Unmarshaller unmarshaller = context.createUnmarshaller();

        Boy boy = new Boy();
        marshaller.marshal(boy, System.out);
        System.out.println();

    }
}

再次运行,结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:b xmlns:ns2="http://test"><age>10</age><name>CY</name></ns2:b>

在生成的xml文件中,<boy> 标签 就会变为 <b> 标签。并且加上一个命名空间。

如果不加名称空间:@XmlRootElement(name="b"),结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><b><age>10</age><name>CY</name></b>

发现<boy> 标签 就会变为 <b> 标签

改造五:

如果不使用@XmlAccessorType指定,@XmlAccessorType的默认访问级别是XmlAccessType.PUBLIC_MEMBER,

@XmlRootElement(name="b")
public class Boy {
    public String name = "CY"; # public访问权限的成员变量

    @XmlElement
    int age=10;
}

@XmlRootElement(name="b")
public class Boy {
    private String name = "CY";
  // 由于name被private修饰,故只能通过getter/setter方式访问的成员变量
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @XmlElement
    int age=10;
}

结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><b><name>CY</name><age>10</age></b>

当为XmlAccessType.PUBLIC_MEMBER:java对象中所有的public访问权限的成员变量和通过getter/setter方式访问的成员变量都可以转为标签。

改造六:

XmlAccessType.PROPERTY:表明只能是属性才能被转为xml中的元素,而@XmlAttribute注解将成员变量映射为属性

@XmlRootElement(name="b")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Boy {
    @XmlAttribute
    private String name = "CY";

    @XmlElement
    int age=10;
}

结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><b name="CY"><age>10</age></b>

发现name成员变量变为了标签的属性

改造七:

@XmlTransient注解忽略被注解的字段

@XmlRootElement(name="b")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Boy {
    @XmlAttribute
    private String name = "CY";

    @XmlTransient
    int age=10;
}

结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><b name="CY"/>

改造八:

添加一个Key类

@XmlRootElement
public class Key {
    @XmlElement
    private String roomNo;

    public Key() {
    }

    public Key(String roomNo) {
        this.roomNo = roomNo;
    }
}

修改Boy类如下:

@XmlRootElement(name="b")
public class Boy {
    @XmlElement
    private String name;

    @XmlElement
    int age;

    public Boy() {
    }
    @XmlElement
    private Set<Key> key = new HashSet<>();

    public Boy(String name, int age) {
        this.name = name;
        this.age = age;
        key.add(new Key("001"));    //向集合中添加两个Key对象
        key.add(new Key("002"));

    }
}

修改测试类如下:

public class JAXBTest {
    public static void main(String[] args) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(Boy.class);

        Marshaller marshaller = context.createMarshaller();
        Unmarshaller unmarshaller = context.createUnmarshaller();

        Boy boy = new Boy("CY",10);
        marshaller.marshal(boy, System.out);
        System.out.println();

    }
}

当不加@XmlElementWrapper(name="keys")注解时,结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<b>
    <name>CY</name>
    <age>10</age>
    <key>
        <roomNo>001</roomNo>
    </key>
    <key>
        <roomNo>002</roomNo>
    </key>
</b>

当加上@XmlElementWrapper(name="keys")后代码如下:

@XmlRootElement(name="b")
public class Boy {
    @XmlElement
    private String name;

    @XmlElement
    int age;

    public Boy() {
    }
    @XmlElementWrapper(name="keys")
    @XmlElement
    private Set<Key> key = new HashSet<>();

    public Boy(String name, int age) {
        this.name = name;
        this.age = age;
        key.add(new Key("001"));    //向集合中添加两个Key对象
        key.add(new Key("002"));

    }
}

结果为:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<b>
    <name>CY</name>
    <age>10</age>
    <keys>
        <key>
            <roomNo>001</roomNo>
        </key>
        <key>
            <roomNo>002</roomNo>
        </key>
    </keys>
</b>

改造九:

在Boy类中添加一个Date类型字段

@XmlRootElement(name="b")
public class Boy {
    @XmlElement
    private String name;

    @XmlElement
    int age;
    @XmlElement
    private Date date = new Date();

    public Boy() {
    }
    @XmlElementWrapper(name="keys")
//    @XmlElement
    private Set<Key> key = new HashSet<>();

    public Boy(String name, int age) {
        this.name = name;
        this.age = age;
        key.add(new Key("001"));    //向集合中添加两个Key对象
        key.add(new Key("002"));

    }
}

结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<b>
    <name>CY</name>
    <age>10</age>
    <date>2021-09-27T17:26:41.525+08:00</date>
    <keys>
        <key>
            <roomNo>001</roomNo>
        </key>
        <key>
            <roomNo>002</roomNo>
        </key>
    </keys>
</b>

我们需要yyyy-MM-dd格式的日期,这就需要@XmlJavaTypeAdapter注解,自定义一个适配器来解决这个问题。

自定义适配器继承XmlAdapter类,实现里面的marshal和unmarshal方法

public class DateAdapter extends XmlAdapter<String, Date> {
    private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
    @Override
    public Date unmarshal(String date) throws Exception {
        return SDF.parse(date);
    }

    @Override
    public String marshal(Date date) throws Exception {
        return SDF.format(date);
    }
}

添加@XmlJavaTypeAdapter(DateAdapter.class)

@XmlRootElement(name="b")
public class Boy {
    @XmlElement
    private String name;

    @XmlElement
    int age;
    @XmlElement
    @XmlJavaTypeAdapter(DateAdapter.class)
    private Date date = new Date();

    public Boy() {
    }
    @XmlElementWrapper(name="keys")
//    @XmlElement
    private Set<Key> key = new HashSet<>();  // key即为被keys标签包装的标签的名字

    public Boy(String name, int age) {
        this.name = name;
        this.age = age;
        key.add(new Key("001"));    //向集合中添加两个Key对象
        key.add(new Key("002"));

    }
}

结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<b>
    <name>CY</name>
    <age>10</age>
    <date>2021-09-27</date>
    <keys>
        <key>
            <roomNo>001</roomNo>
        </key>
        <key>
            <roomNo>002</roomNo>
        </key>
    </keys>
</b>

 改造十:

设置输出的格式:换行和缩进  marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

public class JAXBTest {
    public static void main(String[] args) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(Boy.class);

        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

        Boy boy = new Boy("CY",10);
        marshaller.marshal(boy, System.out);
        System.out.println();
    }
}

结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<b>
    <name>CY</name>
    <age>10</age>
    <date>2021-09-27</date>
    <keys>
        <key1>
            <roomNo>001</roomNo>
        </key1>
        <key1>
            <roomNo>002</roomNo>
        </key1>
    </keys>
</b>

 改造十一:

去掉生成xml时的默认报文头:marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

public class JAXBTest {
    public static void main(String[] args) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(Boy.class);

        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

        Boy boy = new Boy("CY",10);
        marshaller.marshal(boy, System.out);
        System.out.println();
    }
}

结果如下:

<b>
    <name>CY</name>
    <age>10</age>
    <date>2021-09-27</date>
    <keys>
        <key1>
            <roomNo>001</roomNo>
        </key1>
        <key1>
            <roomNo>002</roomNo>
        </key1>
    </keys>
</b>
原文地址:https://www.cnblogs.com/zwh0910/p/15343181.html