JAXB:java对象和xml之间转换

java

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>

 

以上是 JAXB:java对象和xml之间转换 的全部内容, 来源链接: utcz.com/z/389689.html

回到顶部