文章21 | 阅读 8245 | 点赞0
摘要: JAXB 作为JDK的一部分,能便捷地将Java对象与XML进行相互转换,本教程从实际案例出发来讲解JAXB 2 的那些事儿。完整版目录
前面介绍的都是基于最基本的编组过程。为了减少代码量,我接下来使用 JAXB
的静态方法演示编组过程。
@Test public void test1() throws JAXBException { Fruit fruit = new Fruit(); fruit.setColor("red"); JAXB.marshal(fruit, System.out); }
使用的 Fruit
只有一个字段,并且加了注解 @XmlRootElement(name = "水果")
@XmlRootElement(name = "水果") public class Fruit { private String color; // setters,getters }
得到的XML也很简单。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<水果>
<color>red</color>
</水果>
已知:如果 @XmlRootElement
不指定参数,则使用类名首字母小写作为根节点,如果指定name
参数则使用其值作为根节点。
场景假设:XML的根节点需要根据业务场景变化,上例中的<水果>
可以是任何传入的值,那么现有的方案无法实现这样的场景。
解决办法:需要使用到 JAXBElement
,它可以代指任意 XML Element,并且在其初始化时,需要指定几个重要参数。
@Test public void test2() throws JAXBException { Fruit fruit = new Fruit(); fruit.setColor("red"); JAXBElement<Fruit> element = new JAXBElement<Fruit>(new QName("新鲜水果"), Fruit.class, fruit); JAXB.marshal(element, System.out); }
和上例的不同点在于编组的是 JAXBElement
,而不直接作用于 Fruit
,其第一个参数 QName
就是指定根节点的名字,第二个参数指定需要编组的对象,第三个参数是真正的数据。要注意最后一行代码,传入的参数是 element
。
得到的结果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<新鲜水果>
<color>red</color>
</新鲜水果>
如果改一点代码:
@Test public void test2_2() throws JAXBException { GreenFruit fruit = new GreenFruit(); fruit.setColor("Green"); JAXBElement<GreenFruit> element = new JAXBElement<GreenFruit>(new QName("绿色水果"), GreenFruit.class, fruit); JAXB.marshal(element, System.out); }
得到的结果就是代码中设置的 QName
。其实在 Fruit
类上以已经包含注解@XmlRootElement(name = "水果")
,这里设置的值直接覆盖之前注解的name
。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<绿色水果>
<color>Green</color>
</绿色水果>
可能你已经发现了,这里用到了GreenFruit
而不是之前的Fruit
,其实它们有相同的字段,只是 GreenFruit
直接没有加注解 @XmlRootElement
,因为这个注解在这里所起的作用已经被 JAXBElement<>
所替代了。
public class GreenFruit { private String color; // setters,getters }
既然使用 JAXBElement
可以动态指定参数值,如果某个Java 字段使用该类型是否可以做到动态生成XML子节点呢: Yes & No。
定义一个零食,第二个参数是 JAXBElement
的 水果,Fruit
在之前一定定义过了。
@XmlRootElement public class Food { private String name; private JAXBElement<Fruit> element; // setters,getters }
这里还需要指定一个 ObjectFactory
,ObjectFactory 类型的类里面可以定义一些创建某种类型的对象的方法,@XmlRegistry
用于标注在充当ObjectFactory角色的类上,@XmlElementDecl
声明对应的元素定义,其方法的返回值需要是JAXBElement类型,并且它必须指定一个name
,这个name自由赋值,这里指定为’ref1’备用。
customElement 方法我直接返回null,因为实现细节不需要在这里写死,等下创建对象的时候再声明。
@XmlRegistry public class ObjectFactory { @XmlElementDecl(name = "ref1") public JAXBElement<Fruit> customElement(Fruit fruit){ return null; } }
在Food
中定义了 JAXBElement<Fruit>
,需要使用 @XmlElementRef(name="ref1")
关联使用到了 ObjectFactory
哪个方法,可以把@XmlElementRef(name="ref1")
标注在对应的setter/getter方法上,或者标注在字段上,不过需要注意的是标注在字段上,还需要指定@XmlAccessorType(XmlAccessType.FIELD)
.
我习惯将注解标注在字段上,所以需要加@XmlAccessorType
,如果加在get方法上就不需要加@XmlAccessorType
.
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Food { private String name; @XmlElementRef(name="ref1") private JAXBElement<Fruit> element; // setters,getters // @XmlElementRef(name="ref1") public JAXBElement<Fruit> getElement() { return element; } }
测试一下上面的写法是否正确。
@Test public void test4() throws JAXBException { Fruit fruit = new Fruit(); fruit.setColor("red"); JAXBElement<Fruit> element = new JAXBElement<Fruit>(new QName("时令水果"), Fruit.class, fruit); Food food = new Food(); food.setName("Some foods"); food.setElement(element); JAXBContext context = JAXBContext.newInstance(Fruit.class,Food.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(food, System.out); }
可以看到XML的子节点Fruit
并不是之前指定的@XmlRootElement
,而是测试代码中设置的值。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <food> <name>Some foods</name> <时令水果> <color>red</color> </时令水果> </food>
更改QName
的值为‘生鲜水果’,发现生成的XML跟着变化。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<food>
<name>Some foods</name>
<生鲜水果>
<color>red</color>
</生鲜水果>
</food>
既然 XML 中的节点元素都是对应着 Java 类,可以利用继承关系来动态生成 XML 元素。
‘商品信息’(Product.java)是之前用过的例子,它的第二个字段是引用类型:
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Product { @XmlAttribute private String id; @XmlElementRef private Fruit fruit; // setters,getters }
‘水果’(Fruit.java)只有一个字段,并且已经设置了别名@XmlRootElement(name = "水果")
:
@XmlRootElement(name = "水果") public class Fruit { private String color; // setters,getters }
‘水果1’()继承了‘水果’,并且有一个特殊字段:
@XmlRootElement public class Pomelo extends Fruit{ private String name; // setters,getters }
‘水果2’()继承了‘水果’,并且有一个特殊字段:
@XmlRootElement public class Watermelon extends Fruit{ private String shape; // setters,getters }
当商品信息是第一种水果时:
@Test public void test5() throws JAXBException { Pomelo pomelo = new Pomelo(); pomelo.setName("柚子"); pomelo.setColor("Orange"); Product product = new Product(); product.setFruit(pomelo); product.setId("1205"); JAXBContext context = JAXBContext.newInstance(Product.class,Pomelo.class,Fruit.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(product, System.out); }
生成的 XML 如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product id="1205">
<pomelo>
<color>Orange</color>
<name>柚子</name>
</pomelo>
</product>
换一种水果再看看:
@Test public void test5_2() throws JAXBException { Watermelon watermelon = new Watermelon(); watermelon.setShape("椭圆形"); watermelon.setColor("Green"); Product product = new Product(); product.setFruit(watermelon); product.setId("1205"); JAXBContext context = JAXBContext.newInstance(Product.class,Watermelon.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(product, System.out); }
生成的 XML 如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product id="1205">
<watermelon>
<color>Green</color>
<shape>椭圆形</shape>
</watermelon>
</product>
商品信息每次根据不同的子商品而变化,之前已经设置过的主商品Fruit
已经不能影响最终结果。
需要注意的是,这里不能直接使用静态工具类JAXB
,下面的方式生成的结果不正确:
@Test public void test5_3() throws JAXBException { Watermelon watermelon = new Watermelon(); watermelon.setShape("椭圆形"); watermelon.setColor("Green"); Product product = new Product(); product.setFruit(watermelon); product.setId("1205"); JAXB.marshal(product, System.out); }
得到的 XML 和之前的预期不一致:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product id="1205">
<水果>
<color>Green</color>
</水果>
</product>
因为 JAXB 工具类在注册newInstance
时,只关注第一个参数JAXB.marshal(object, out)
,而这里的第一个参数是Product,因此不能注册Fruit的子类 Watermelon,所有与 Watermelon 相关的设置都不能成功,不过这里与父类 Fruit 相关的设置都生效了。
可以在GitHub找到完整代码。
本节代码均在该包下:package com.example.demo.lesson11;
本节介绍了 JAXB 编组为复杂 XML 的场景,下一节还将关注于实用的场景。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://jiangchao.blog.csdn.net/article/details/82941043
内容来源于网络,如有侵权,请联系作者删除!