JAXB 中的@XmlElementWrapper注解生成问题
本文同步发表在 http://www.simonme.org/?p=220
1. 面临的问题场景
<?xml version="1.0" encoding="UTF-8"?> <root xmlns="http://www.example.org/JaxbEleWrapperSchema"> <students> <student sno="001" name="Simon" /> <student sno="002" name="Eva" /> </students> </root>
当我们需要用JAXB处理(生成或读取)这样的xml文件时,根据xjc默认生成的代码,会需要这样去使用
@Test public void test() { Root root = new Root(); Student stu = new Student(); stu.setName("Simon"); stu.setSno("001"); Student stu1 = new Student(); stu1.setName("Eva"); stu1.setSno("002"); Students students = new Students(); students.getStudent().add(stu); students.getStudent().add(stu1); root.setStudents(students); String testFilename = "d:/test.xml"; JAXBUtils.object2xmlFile(root, testFilename); }
这样代码看起来貌似不太理想,主要是针对Students 对象处理的地方,貌似有点多余,其实本意上就是想用<students>这个标签把<student>元素包起来,无其他用处。怎样解决这个貌似多余代码的问题呢? JAXB提供了@XmlElementWrapper注解来做这个事情。
2. 用@XmlElementWrapper注解使代码更精致
将Root类的代码改造成这样:
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Root") public class Root { @XmlElementWrapper(name="students") @XmlElement(required = true) protected List<Student> student; public List<Student> getStudent() { if(student == null) { student = new ArrayList<Student>(); } return student; } public void setStudent(List<Student> student) { this.student = student; } }
这样测试代码只需要写成
@Test public void test() { Root root = new Root(); Student stu = new Student(); stu.setName("Simon"); stu.setSno("001"); Student stu1 = new Student(); stu1.setName("Eva"); stu1.setSno("002"); root.getStudent().add(stu); root.getStudent().add(stu1); String testFilename = "d:/test.xml"; JAXBUtils.object2xmlFile(root, testFilename); }
这样更精致,但是又一个问题来了,一般我们用JAXB,代码都是用xjc命令行或者xjc的ant task生成,如下:
<?xml version="1.0" encoding="UTF-8"?> <project name="test" default="run" basedir="."> <taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask"> <classpath> <fileset dir="D:/004.work/02.tools/jaxb-ri-2.2.6/jaxb-ri-2.2.6/lib" includes="*.jar" /> </classpath> </taskdef> <target name="run"> <xjc destdir="D:/004.work/03.workspace/02.plugin_dev_demo/JaxbElementWarp/src" package="com.xxx.xxx.demo.xsd.test" > <schema dir="D:/004.work/03.workspace/02.plugin_dev_demo/JaxbElementWarp/src" includes="JaxbEleWrapper.xsd" /> </xjc> </target> </project>
但是这个我们手工干预的貌似很麻烦,如何让他自动生成@XmlElementWrapper这个注解,并对代码做相应调整。
3. 如何用xjc生成@XmlElementWrapper注解
需要使用github上的这个jaxb-xew-plugin.jarxjc插件(https://github.com/wumpz/jaxb-xew-plugin)。下面介绍如何使用。
首先这个插件需要匹配JAXB 2.2版本,目前还不兼容于JAXB2.0版本,如果使用2.0版本,会出现NoSuchMethod之类的异常。去jaxb官网找到类似jaxb-ri-2.2.6 的zip包
引用。
编写ant脚本如下:
<?xml version="1.0" encoding="UTF-8"?> <project name="test" default="run" basedir="."> <taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask"> <classpath> <fileset dir="D:/004.work/02.tools/jaxb-ri-2.2.6/jaxb-ri-2.2.6/lib" includes="*.jar" /> <fileset dir="D:/004.work/02.tools" includes="jaxb-xew-plugin.jar" /> </classpath> </taskdef> <target name="run"> <xjc destdir="D:/004.work/03.workspace/02.plugin_dev_demo/JaxbElementWarp/src" package="com.xxx.xxx.demo.xsd.test" > <arg value="-Xxew" /> <!-- <arg value="-Xxew:instantiate lazy" /> --> <schema dir="D:/004.work/03.workspace/02.plugin_dev_demo/JaxbElementWarp/src" includes="JaxbEleWrapper.xsd" /> </xjc> </target> </project>
具体用法可以参见https://github.com/wumpz/jaxb-xew-plugin文档。
这个插件目前能做到我们想要的效果,但是多余的Students类仍然会生成,但是无碍功能,可以手动删除,并在ObjectFactory类中去掉对其的引用。
4. jaxb-xew-plugin一点思考
目前的玩法是先在xsd中定义上studetns这样的元素(把下面的student子元素包起来的元素),然后再用jaxb-xew-plugin插件生成。
而不是:我们不在xsd文件中定义students元素,仅仅凭student元素是0到多个或者1到多个来生成@XmlElementWrapper注解。