JAXB动态指定生成的XML元素名称
通常我们在使用JAXB生成XML时,都是通过@XmlRootElement
或@XmlElement
事先指定对应的类型的对象在生成XML时生成的元素的名称。比如下面这样。
@XmlRootElement(name="person")
@XmlType(propOrder = { "id", "name", "age", "address" })
public class Person {
private Integer id;
private String name;
private Integer age;
private Address address;
@XmlAttribute(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
@XmlType(propOrder = { "province", "city", "area", "other" })
public class Address {
private Integer id;
private String province;
private String city;
private String area;
private String other;
@XmlAttribute(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getOther() {
return other;
}
public void setOther(String other) {
this.other = other;
}
}
对应的输出结果是如下这样,这样的结果大家都已经很熟悉了。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person id="1" name="张三">
<age>30</age>
<address id="1">
<province>广东省</province>
<city>深圳市</city>
<area>南山区</area>
<other>其它</other>
</address>
</person>
动态生成根节点元素名称
那如果现在业务上有这样一项需求,Person类对应的根节点元素的名称需要是动态的,某些业务场景下是person1,某些业务场景下是person2、person3等这应该怎么办呢?这个时候我们应该把我们的对象转换为一个JAXBElement
对象,JAXB在把JAXBElement
类型的对象转换为XML时会调用它的getName()
方法获取对应的元素的名称。我们把Person类改造一下,新增一个elementName属性,用于指定动态的节点名称,即生成的根节点的名称将以该属性值为准,同时该属性不属于Person类对应XML元素的子元素的一部分,所以需要加上@XmlTransient。
@XmlType(propOrder={"id", "name", "age", "address"})
public class Person {
private Integer id;
private String name;
private Integer age;
private Address address;
/**
* 用来指定对应的根节点的名称
*/
private String elementName;
@XmlAttribute(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
/**
* 该属性主要用来指定对象的根节点名称,不需要作为对应转换为XML的一个元素,所以需要使用@XmlTransient标注
* @return
*/
@XmlTransient
public String getElementName() {
return elementName;
}
public void setElementName(String elementName) {
this.elementName = elementName;
}
}
然后在生成XML时把Person对象使用JAXBElement对象包裹起来,marshal的对象也由Person对象变为包裹了Person对象的JAXBElement对象,所以这时候的marshal代码如下。
@Test
public void testMarshal() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Person.class, ObjectFactory.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
//构造Person对象,不是本文的重点
Person person = this.buildPerson();
//这里可以根据业务需要动态的指定元素的名称
person.setElementName("person1");
String eleName = person.getElementName();
QName name = new QName(eleName);
JAXBElement<Person> personEle = new JAXBElement<>(name, Person.class, person);
marshaller.marshal(personEle, writer);
System.out.println(writer.toString());
}
生成的XML如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person1 id="1" name="张三">
<age>30</age>
<address id="1">
<province>广东省</province>
<city>深圳市</city>
<area>南山区</area>
<other>其它</other>
</address>
</person1>
这个时候Person类上的
@XmlRootElement
已经不起作用了,可有可无,因为XML元素名称已经不由它来指定了,但是属性上的@XmlAttribute
和@XmlElement
还是会起作用的。
动态生成非根节点元素名称
如果上面的Person类的Address属性也有动态节点的需求,也是根据某种业务规则需要生成address1、address2等节点名称,这个时候又该怎么做呢?你会不会觉得这很简单,我把对应的属性定义为JAXBElement
类型即可,像如下这样子。
@XmlType(propOrder={"id", "name", "age", "address"})
public class Person {
private Integer id;
private String name;
private Integer age;
private JAXBElement<Address> address;
/**
* 用来指定对应的根节点的名称
*/
private String elementName;
@XmlAttribute(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public JAXBElement<Address> getAddress() {
return address;
}
public void setAddress(JAXBElement<Address> address) {
this.address = address;
}
/**
* 该属性主要用来指定对象的根节点名称,不需要作为对应转换为XML的一个元素,所以需要使用@XmlTransient标注
* @return
*/
@XmlTransient
public String getElementName() {
return elementName;
}
public void setElementName(String elementName) {
this.elementName = elementName;
}
}
然后在Address类中也增加一个elementName属性用于指定动态的节点名称。
@XmlType(propOrder = { "province", "city", "area", "other" })
public class Address {
private Integer id;
private String province;
private String city;
private String area;
private String other;
/**
* 用来指定根节点名称
*/
private String elementName;
@XmlAttribute(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getOther() {
return other;
}
public void setOther(String other) {
this.other = other;
}
@XmlTransient
public String getElementName() {
return elementName;
}
public void setElementName(String elementName) {
this.elementName = elementName;
}
}
在构造Person对象时,把构造的Address对象用JAXBElement对象包起来再赋给Person对象。
private Person buildPerson() {
Person person = new Person();
person.setId(1);
person.setName("张三");
person.setAge(30);
Address address = new Address();
address.setId(1);
address.setProvince("广东省");
address.setCity("深圳市");
address.setArea("南山区");
address.setOther("其它");
//这里可以根据业务需要动态的指定元素的名称
address.setElementName("address1");
QName name = new QName(address.getElementName());
JAXBElement<Address> addressEle = new JAXBElement<>(name, Address.class, address);
person.setAddress(addressEle);
return person;
}
在进行marshal创建JAXBContext时需要加入Address.class,不然在marshal时将不识别Address类。其它的保持不变。
@Test
public void testMarshal() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Person.class, Address.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
//构造Person对象,不是本文的重点
Person person = this.buildPerson();
//这里可以根据业务需要动态的指定元素的名称
person.setElementName("person1");
String eleName = person.getElementName();
QName name = new QName(eleName);
JAXBElement<Person> personEle = new JAXBElement<>(name, Person.class, person);
marshaller.marshal(personEle, writer);
System.out.println(writer.toString());
}
这时候生成的XML如下所示。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person1 id="1" name="张三">
<age>30</age>
<address>
<nil>false</nil>
<value xsi:type="address" id="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<province>广东省</province>
<city>深圳市</city>
<area>南山区</area>
<other>其它</other>
</value>
</address>
</person1>
其实这也很好理解,JAXB把JAXBElement当作一个普通的Java类来处理了,其中拥有setNil()和isNil()方法,所以会产生一个nil节点;它也拥有getValue()和setValue()的方法,所以产生了value节点;value节点的真实Java类型是Address类型,所以value节点的子节点都是按照Address类的结构来生成的;至于value节点上的namespace信息是因为Address类没有直接跟根节点对应的类Person产生关联,所以需要额外的信息来声明。显然,这与我们期望的结果是不一样的,我们本来期望的是得到如下这样的内容。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person1 id="1" name="张三">
<age>30</age>
<address1 id="1">
<province>广东省</province>
<city>深圳市</city>
<area>南山区</area>
<other>其它</other>
</address1>
</person1>
那需要如何才能得到我们想要的结果呢?这时候就需要应用@XmlRegistry、@XmlElementDecl和@XmlElementRef了。
- XmlRegistry注解用来标注在类上面,表示该类中包含有使用@XmlElementDecl标注的方法。
- XmlElementDecl注解用来标注在方法上,该方法可以接收参数,但需要有返回类型。它又需要与@XmlElementRef一起使用。XmlElementDecl在使用时必须指定一个name属性,namespace属性也可以指定,以唯一的确定这样一个XmlElementDecl的声明。
- 我们可以通过XmlElementRef来指定当前的XML和对象之间的映射需要使用的是哪个XmlElementDecl指定的方法,这是通过其name属性和namespace属性来指定的,它们的值必须与对应的XmlElementDecl上声明的name和namespace一致。
当我们的一个属性声明为JAXBElement形式,我们希望它生成的XML形式是以JAXBElement中包含的那个具体的对象为准,而不是以JAXBElement自己为准时,我们可以在该属性上使用@XmlElementRef标注,同时指向创建它的ObjectFactory的对应方法上标注的@XmlElementDecl,同时需要注意在ObjectFactory类上采用@XmlRegistry进行标注。然后在转换XML时,还需要指定使用的Class除了当前的对象外,还包括对应的ObjectFactory类。所以针对上面的需求,我们可以改为如下配置即可。
@XmlType(propOrder={"id", "name", "age", "address"})
public class Person {
private Integer id;
private String name;
private Integer age;
private JAXBElement<Address> address;
/**
* 用来指定对应的根节点的名称
*/
private String elementName;
@XmlAttribute(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@XmlElementRef(name="address")
public JAXBElement<Address> getAddress() {
return address;
}
public void setAddress(JAXBElement<Address> address) {
this.address = address;
}
/**
* 该属性主要用来指定对象的根节点名称,不需要作为对应转换为XML的一个元素,所以需要使用@XmlTransient标注
* @return
*/
@XmlTransient
public String getElementName() {
return elementName;
}
public void setElementName(String elementName) {
this.elementName = elementName;
}
}
@XmlRegistry
public class ObjectFactory {
@XmlElementDecl(name="address")
public JAXBElement<Address> createAddress() {
return null;
}
}
上面的ObjectFactory的createAddress()方法是一个空实现,直接返回了null。实际上该方法主要用来作为JAXB的一个规范使用的,主要是用来定义XmlElementDecl的。当然了,如果有需要你也可以在其中定义创建对象的真实逻辑,该方法也可以接收参数。
在创建对应的JAXBContext时加上我们所使用的ObjectFactory.class。
@Test
public void testMarshal() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Person.class, ObjectFactory.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
//构造Person对象,不是本文的重点
Person person = this.buildPerson();
//这里可以根据业务需要动态的指定元素的名称
person.setElementName("person1");
String eleName = person.getElementName();
QName name = new QName(eleName);
JAXBElement<Person> personEle = new JAXBElement<>(name, Person.class, person);
marshaller.marshal(personEle, writer);
System.out.println(writer.toString());
}
应用上面的测试代码,最终的生成结果是如下这样的,这就达到了我们的期望。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person1 id="1" name="张三">
<age>30</age>
<address1 id="1">
<province>广东省</province>
<city>深圳市</city>
<area>南山区</area>
<other>其它</other>
</address1>
</person1>
(完)
相关推荐
android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台
一个简洁而又优雅的Android原生UI框架,解放你的双手!还不赶紧点击使用说明文档,体验一下吧! 涵盖绝大部分的UI组件:TextView、Button、EditText、ImageView、Spinner、Picker、Dialog、PopupWindow、ProgressBar、LoadingView、StateLayout、FlowLayout、Switch、Actionbar、TabBar、Banner、GuideView、BadgeView、MarqueeView、WebView、SearchView等一系列的组件和丰富多彩的样式主题。
基于背向散射的超声骨密度仪算法研究和软件设计的任务书.docx
机械毕业设计81五自由度机械臂设计.doc
数据可视化-上海各地区风速热力图
android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台
RBF神经网络概述.doc
游戏植物大战僵尸(经典版)
1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
基于s7200自动售货机-plc-控制.doc
基于深度学习方法去评估锂电池健康状态(SOH)python源码.zip对象是NASA的锂电池容量衰退数据集,本资源中的源码都是经过本地编译过可运行的,评审分达到95分以上。资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 基于深度学习方法去评估锂电池健康状态(SOH)python源码.zip对象是NASA的锂电池容量衰退数据集,本资源中的源码都是经过本地编译过可运行的,评审分达到95分以上。资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。基于深度学习方法去评估锂电池健康状态(SOH)python源码.zip对象是NASA的锂电池容量衰退数据集,本资源中的源码都是经过本地编译过可运行的,评审分达到95分以上。资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 基于深度学习方法去评估锂电池健康状态(SOH)python源码.zip对象是NASA的锂电池容量衰退数据集,本资源中的源码都是经过本地编译过可运行的,评审分
电脑工具.txt
1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台
移动机器人机械臂的设计小论文.doc
android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台
android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台
C语言诞生于美国的贝尔实验室,由丹尼斯·里奇(Dennis MacAlistair Ritchie)以肯尼斯·蓝·汤普森(Kenneth Lane Thompson)设计的B语言为基础发展而来,在它的主体设计完成后,汤普森和里奇用它完全重写了UNIX,且随着UNIX的发展,c语言也得到了不断的完善。为了利于C语言的全面推广,许多专家学者和硬件厂商联合组成了C语言标准委员会,并在之后的1989年,诞生了第一个完备的C标准,简称“C89”,也就是“ANSI C”,截至2020年,最新的C语言标准为2018年6月发布的“C18”。 [5] C语言之所以命名为C,是因为C语言源自Ken Thompson发明的B语言,而B语言则源自BCPL语言。 1967年,剑桥大学的Martin Richards对CPL语言进行了简化,于是产生了BCPL(Basic Combined Programming Language)语言。
【资源说明】 C++基于OpenCV对图片进行识别+裁剪源码+使用文档+全部资料(优秀项目).zipC++基于OpenCV对图片进行识别+裁剪源码+使用文档+全部资料(优秀项目).zipC++基于OpenCV对图片进行识别+裁剪源码+使用文档+全部资料(优秀项目).zip 【备注】 1、该项目是个人高分毕业设计项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(如软件工程、计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!
密码学实验报告2.docx