文章21 | 阅读 8241 | 点赞0
摘要: JAXB 作为JDK的一部分,能便捷地将Java对象与XML进行相互转换,本教程从实际案例出发来讲解JAXB 2 的那些事儿。完整版目录
上一节介绍的是关于Map转换方式,这一节开始,将基于Java Interface 接口做转换。
对于XML来说,接口是 Java 特有的数据形态,直接将Java的接口转化为XML结构是不可能的,但是可以通过间接的方式实现。
现在,有一个’动物园’中包含很多动物。
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Zoo {
@XmlAnyElement
public List<Animal> animals;
// ignore setter/getter
}
并不知道有什么动物,我们使用接口来定义 Animal
。需要注意的是,这里需要使用注解@XmlAnyElement
来标注接口。
public interface Animal {
void eat();
void sleep();
}
动物1号 ‘狗’ 出场
@XmlRootElement
public class Dog implements Animal{
}
动物2号 ‘猫’ 出场
@XmlRootElement
public class Cat implements Animal{
}
@Test
public void test1() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Zoo.class,Dog.class,Cat.class);
Zoo zoo = new Zoo();
zoo.setAnimals(Arrays.asList(new Dog(), new Cat()));
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(zoo, System.out);
}
生成的XML结果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<zoo>
<dog/>
<cat/>
</zoo>
这里的 newInstance
中只指定了类不用指定接口。
@Test
public void test1_1() throws JAXBException {
Zoo zoo = new Zoo();
zoo.setAnimals(Arrays.asList(new Dog(), new Cat()));
JAXB.marshal(zoo, System.out);
}
这是使用静态方法实现的,之前的很多例子都是这样写的,但是对于复杂的类型,不能采用这种方式,需要显示指定所有需要处理的类给 JAXBContext.newInstance
在处理接口过程中,有时候可能会看到如下异常信息:com.sc.md.datatypes.schemas.csemessage.EnvelopeType is an interface, and JAXB can't handle interfaces.
或者这样 com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions com.example.demo.lesson15.Animal是接口, 而 JAXB 无法处理接口。 al.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
错误分析:
在上面的接口集合上,加了一个注解@XmlAnyElement
,该注解起着至关重要的作用,告诉JAXB这是一个不确定类型的属性。
之前很少使用到 @XmlAnyElement,因为一般的Java属性都明确知道类型,但是下面几种写法可能使JAXB懵逼。
@XmlAnyElement
public Element[] others;
@XmlAnyElement(lax="true")
public Object[] others;
@XmlAnyElement
private List<Element> nodes;
采用 @XmlAnyElement
可以模糊化数据类型,相当于一种万能的注解,因为可以标注任意元素。但是,它也有诸多限制。
采用适配器需要比较多的代码,但是在处理复杂的数据类型方面,它无所不能,之前在处理Map的时候,已经使用过,这里简单演示。
食物有多种类型,其中一种是水果:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Food {
public Fruit fruit1;
public Fruit fruit2;
// ignore setters/getters
}
水果有多种形态,采用接口形式,简单起见,接口留空,不过使用注解@XmlJavaTypeAdapter
来声明使用MyAdapter
处理转化逻辑:
@XmlJavaTypeAdapter(MyAdapter.class)
public interface Fruit {
}
西瓜是我最爱吃的水果之一,它有一个属性,标明其颜色:
public class WaterMelon implements Fruit{
public WaterMelon() {
}
public WaterMelon(String color) {
this.color = color;
}
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
适配器很简单,直接返回对应的数据,不做任何特殊处理:
public class MyAdapter extends XmlAdapter<WaterMelon, Fruit>{
@Override
public Fruit unmarshal(WaterMelon v) throws Exception {
return v;
}
@Override
public WaterMelon marshal(Fruit v) throws Exception {
return (WaterMelon) v;
}
}
@Test
public void test2() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Food.class,WaterMelon.class);
Food food = new Food();
food.setFruit1(new WaterMelon("Green"));
food.setFruit2(new WaterMelon("Red"));
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(food, System.out);
}
得到的XML数据:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<food>
<fruit1>
<color>Green</color>
</fruit1>
<fruit2>
<color>Red</color>
</fruit2>
</food>
这种方式有几个好处:
上面的例子中,使用到了接口,而Java中在介于类和接口之间还有一种形态——抽象类。
工人有多种角色,有的是雇主,有的是雇员:
@XmlRootElement
public class Worker {
public Human employe;
public Human employee;
}
无论是管理者还是普通员工,都是由人扮演的:
@XmlJavaTypeAdapter(ManAdapter.class)
public interface Human {
}
人类可以按照多种方式分别,性别是最简单的一种:
public class Man implements Human{
public String name;
public int age;
}
public class Woman implements Human{
public String name;
public double salary;
}
男人和女人都是Human,他们很多不同,但又诸多一样,于是将其抽象出来。
public abstract class AbstractMan implements Human{
public String name;
}
这样,可以修改已经定义好的并且有重复属性的男人女人们:
public class Woman extends AbstractMan{
public int age;
}
把他们之间不同的部分定义在自己的类中:
public class Man extends AbstractMan{
public double salary;
}
适配器还和上面例子一样,直接返回:
public class ManAdapter extends XmlAdapter<AbstractMan, Human>{
@Override
public Human unmarshal(AbstractMan v) throws Exception {
return v;
}
@Override
public AbstractMan marshal(Human v) throws Exception {
return (AbstractMan) v;
}
}
@Test
public void test3() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Worker.class,Man.class,Woman.class);
Worker worker = new Worker();
Man man = new Man();
man.name = "Zhangsan";
worker.employe = man;
Woman woman = new Woman();
woman.age = 24;
worker.employee = woman;
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(worker, System.out);
}
得到的XML数据:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worker>
<employe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="man">
<name>Zhangsan</name>
</employe>
<employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="woman">
<age>24</age>
</employee>
</worker>
现在的Adapter还不够通用,因为没有什么逻辑,只返回自己的话,可以考虑Java中的Object这个‘万能’类型。
public class AnyTypeAdapter extends XmlAdapter<Object, Object>{
@Override
public Object unmarshal(Object v) throws Exception {
return v;
}
@Override
public Object marshal(Object v) throws Exception {
return v;
}
}
这样就可以处理任意的数据类型,传入的是Object,只要是Java对象,都能转换了。
为了验证我的说法,可以对代码稍作改动:
@XmlJavaTypeAdapter(AnyTypeAdapter.class)
public interface Human {
}
这样就把转换工作交给了 AnyTypeAdapter
,不需要改动其他代码,发现能得到相同的结果。
可以在GitHub找到完整代码。
本节代码均在该包下:package com.example.demo.lesson15;
本节介绍了 JAXB 中 Interface 的相关转化,内容比较多,也是Java转换XML的最后一节。下一节开始,将开始讲述XML转化为Java对象,也就是 Unmarshaller .
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://jiangchao.blog.csdn.net/article/details/84403287
内容来源于网络,如有侵权,请联系作者删除!