JavaBean和XML相互转换(二)
上一篇文章:JavaBean 和 XML 相互转换(一)
在上一篇文章中我们介绍了 JavaBean 和 XML 相互转换使用的工具和相关注解(@XmlRootElement、@XmlType、@XmlAccessorType),在本篇文章中继续学习其他相关注解。
本篇测试使用的 JavaBean 同《JavaBean 和 XML 相互转换(一)》,为了方便大家阅读,在本篇冗余之前的代码,如下:学校类:其包括基本类型字段和集合字段/** * 学校 */ @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class School { /** * 学校名称 */ private String name; /** * 学校联系方式 */ private String phone; /** * 学校地址 */ private String address; /** * 班级 */ private List classes; }班级类:其包括基本类型字段和集合字段/** * 班级 */ @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class ClassInfo { /** * 班级编号 */ private String classNo; /** * 班主任 */ private String teacher; /** * 学生 */ private List students; }学生类:只包括基本类型字段/** * 学生 */ @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class Student { /** * 学号 */ private String no; /** * 学生姓名 */ private String name; /** * 年龄 */ private Integer age; /** * 家庭地址 */ private String address; }@XmlElement
标注位置
FIELD, METHOD, PARAMETER
是否必须
非必须
注解作用JavaBean 属性和 XML 标签相互映射,该注解主要讲解两个属性:name:JavaBean 属性与该属性指定的 XML 标签映射,如果未设置,默认为 JavaBean 属性名称type:当注解标注的属性为接口或父类时,转换时需要指定实现类或子类的类型,否则无法转换
使用该注解的场景一般是 XML 标签与相应的 JavaBean 属性名称不同,或 JavaBean 属性类型为接口或父类测试 name 属性
修改学生类,给 address 属性添加注解,其他不变,如下所示:@Data @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Student { // 省略不变的属性 /** * 家庭地址 */ @XmlElement(name = "ADDRESS") private String address; }
测试代码:public class XmlBeanMapperTest { private static final String XML = "<?xml version="1.0" encoding="UTF-8" standalone="yes"?> " + " " + " 北京市长安街 001 号 " + " 25 " + " 张三 " + " 1001 " + ""; /** * 测试 {@link XmlElement} 注解 */ @Test public void testXmlElement() { Student student = new Student("1001", "张三", 25, "北京市长安街 001 号"); Console.log(JAXBUtil.beanToXml(student)); Console.log(JAXBUtil.xmlToBean(XML, Student.class)); } }
执行测试代码,结果如下:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 1001 张三 25 北京市长安街 001 号 Student(no=1001, name=张三, age=25, address=null)
通过上面测试结果可以看到,JavaBean 转 XML 后,address 属性对应 XML 的标签为 ADDRESS。反之 XML 字符串中 address 标签也无法映射到 address 属性上。测试 type 属性
创建装扮接口 BaseDress:/** * 装扮接口 */ @XmlAccessorType(XmlAccessType.FIELD) public interface BaseDress { }
因为在本示例中使用 Lombok @Data 注解,所以需要将注解 @XmlAccessorType value 设置为 FIELD,而且该注解具有继承性(注解使用 @Inherited 元注解),为了不用每个实现类重复添加,所以在接口上统一添加该注解。
修改学生类,添加类型为 BaseDress 的新属性 dress,如下所示:@Data @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Student { // 不变的属性省略 /** * 装扮 */ @XmlElement(type = NurseDress.class) private BaseDress dress; }
因为 dress 属性的类型是接口,JAXB 无法处理接口类型的属性,所以需要使用 @XmlElement 注解 type 设置对应实现类的类型。这就造成我们无法使用 Java 多态特性。
新增 BaseDress 实现类 NurseDress,如下所示:/** * 护士装扮 */ @Data @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class NurseDress implements BaseDress { /** * 上衣 */ private String blouse; /** * 裙子 */ private String skirt; }
测试代码如下:public class XmlBeanMapperTest { /** * 测试 {@link XmlElement} 注解 */ @Test public void testXmlElement() { Student student = new Student("1001", "张三", 25, "北京市长安街 001 号", new NurseDress("红色上衣", "绿色裙子")); Console.log(JAXBUtil.beanToXml(student)); } }
运行测试类,结果如下:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 1001 张三 25 北京市长安街 001 号 红色上衣 绿色裙子 @XmlElements
在 @XmlElement 测试中,有个问题就是如果类的属性类型为接口或抽象类,必须使用 @XmlElement 注解 type 属性指定实现类或子类类型,否则会抛出异常信息。这样不能使用多态,失去了定义接口或抽象类的意义,有一种妥协的方法就是使用 @XmlElements 注解,如下示例。
在 @XmlElement 测试类基础上,新增 BaseDress 实现类 MaidDress,如下所示:/** * 女仆装扮 */ @Data @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @XmlRootElement(name="jar") public class MaidDress extends BaseDress { /** * 上衣 */ private String blouse; /** * 裙子 */ private String skirt; }
修改学生类,修改 dress 属性注解,如下所示:@Data @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Student { // 不变的属性省略 /** * 装扮 */ @XmlElements({ @XmlElement(type = NurseDress.class), @XmlElement(type = MaidDress.class) }) private BaseDress dress; }
将 @XmlElement 注解更换为 @XmlElements 注解,并设置其 value 属性为可能赋值的 BaseDress 接口实现类类型数组
运行测试类,测试结果如下:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 1001 张三 25 北京市长安街 001 号 红色上衣 粉红色裙子 Student(no=1001, name=张三, age=25, address=北京市长安街 001 号, dress=null)
可以正常执行,只要赋值的类型在指定的 @XmlElement 数组中,就可以映射@XmlTransient
标注位置
FIELD, METHOD, TYPE
是否必须
非必须
注解作用
JavaBean 属性和 XML 标签相互映射是忽略标注该注解的属性
修改学生类,在 address 属性上添加该注解,如下所示:@Data @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Student { // 不变的属性省略 /** * 家庭地址 */ @XmlTransient private String address; }
测试类如下:public class XmlBeanMapperTest { private static final String XML = "<?xml version="1.0" encoding="UTF-8" standalone="yes"?> " + " " + " 北京市长安街 001 号 " + " 25 " + " 张三 " + " 1001 " + ""; /** * 测试 {@link XmlTransient} 注解 */ @Test public void testXmlTransient() { Student student = new Student("1001", "张三", 25, "北京市长安街 001 号"); Console.log(JAXBUtil.beanToXml(student)); Console.log(JAXBUtil.xmlToBean(XML, Student.class)); } }
运行测试类,结果如下:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 1001 张三 25 Student(no=1001, name=张三, age=25, address=null)@XmlAttribute
标注位置
FIELD, METHOD
是否必须
非必须
注解作用
将 JavaBean 的属性与 XML 标签的属性相互映射
修改学生类,在 address 属性上添加该注解,如下所示:@Data @ToString @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Student { // 不变的属性省略 /** * 家庭地址 */ @XmlAttribute private String address; }
测试类同 @XmlTransient 注解中的测试类,运行后结果如下:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 1001 张三 25 Student(no=1001, name=张三, age=25, address=null)
通过上面的结果可以看到学生类 address 属性映射到了 XML student 标签的 address 属性
由于内容较多,其他内容详见下一篇文章,如果文章对大家有所帮助,欢迎点赞、关注、评论。