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>