JAXB-4 Java 类与 XML 的转换

4 Java 类与 XML 的转换

通过前面几章的学习,我们已经掌握了,手动为 Java Bean 添加注解,与简单的 XML 进行相互转换;接下来,探讨多种关联关系在 JAXB 中的实现。

JAXB 支持 Java 对象树与 XML 文档相互转换,有如下几种场景:

  1. 简单 Java 类与 XML 的转换
  2. 一一关联的 Java 类与 XML 的转换
  3. 一对多(List)关联 Java 类与 XML 的转换
  4. 一对多(Map)关联 Java 类与 XML 的转换

4.1 简单 Java 类与 XML 的转换

简单 Java 类,指由基础数据类型(如 Integer, String, Boolean, Double 等)构建而成的 Java 类。
1、首先定义需要转换的 Java bean Student.java

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Student {
    @XmlAttribute
    private String id;
    private String name;
    private Integer age;
	// getters, setters
}

这个对象和我们常见的Java对象唯一的不同,就是在普通的Java类上添加了注解 @XmlRootElement,默认类的属性(get/set)会被识别成 XML 节点元素,通过添加 @XmlAttribute 注解,将属性识别成该类节点的属性值。

2、进行简单 Java 类与 XML 的转换测试

public void test() throws JAXBException {

    Student stu = new Student();
    stu.setId("001");
    stu.setName("Tom");
    stu.setAge(22);
    // Marshaller
    JAXBContext context = JAXBContext.newInstance(Student.class);
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    StringWriter sw = new StringWriter();
    marshaller.marshal(stu, sw);
    System.out.println(sw.toString());
    // UnMarshaller
    Unmarshaller unmarshaller = context.createUnmarshaller();
    StringReader sr = new StringReader(sw.toString());
    Student stu2 = (Student) unmarshaller.unmarshal(sr);
}

这里演示了最基本的Java 对象和XML相互转换的过程。使用 JAXB 不需要引入第三方依赖jar包。在控制台可以看到如下输出。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student id="001">
    <age>22</age>
    <name>Tom</name>
</student>

有几点需要注意:

  • JAXB 转换对象必须属于JAXBElement类型,或者使用 @XmlRootElement注解
  • JAXB 转换对象必须拥有无参数构造器(默认存在,如果被覆盖,需要显示指定)

4.2 一一关联的 Java 类与 XML 的转换

1...1
Cashier
Order

Order对象中包含Product对象,这在项目中是常见情形。Order 对象无需进行特殊的配置,JAXB 能够对 Order 对象嵌套的 Cashier 对象进行解析。
Order对象的第三个属性是个复杂的数据类型。

@XmlRootElement(name = "Order")
@XmlAccessorType(XmlAccessType.FIELD)
public class Order {
    @XmlAttribute
    private String id;
    private Double price;
    private Cashier cashier;
    //setters, getters
}

被嵌套的 Cashier 不需要使用 @XmlRootElement注解。

@XmlAccessorType(XmlAccessType.FIELD)
public class Cashier {
    @XmlAttribute
    private String id;
    private String name;
    //setters, getters
}

编写测试单元进行测试;

public void test() throws JAXBException {
    // Cashier
    Cashier c = new Cashier();
    c.setId("1100");
    c.setName("Tom");
    // Order
    Order order = new Order();
    order.setId("1101");
    order.setPrice(23.45);
    order.setCashier(c);

    // Marshaller
    JAXBContext context = JAXBContext.newInstance(Order.class);
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    StringWriter sw = new StringWriter();
    marshaller.marshal(order, sw);
    System.out.println(sw.toString());
    // UnMarshaller
    Unmarshaller unmarshaller = context.createUnmarshaller();
    StringReader sr = new StringReader(sw.toString());
    Order order2 = (Order) unmarshaller.unmarshal(sr);
}

生成的XML含有两个层级。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order id="1101">
    <cashier id="1100">
        <name>Tom</name>
    </cashier>
    <price>23.45</price>
</Order>

4.3 一对多(List)关联 Java 类与 XML 的转换

Java 对象中含有 List,可以采取如下几种方式:

  1. 采用默认配置处理 List 集合
  2. 采用 @XmlElementWrapper 注解,将集合元素作为指定 XML 节点的次级元素;
  3. 采用 @XmlValue 注解,将非简单 Java 类转换为简单 XML 元素;
  4. 采用 @XmlList 注解,在一个 XML 的 Element 中添加多个值。

泛型 List 集合类型,可以使用基本数据类型,也可以使用自定义类型;在 XML 的转换中,基础数据类型,如 String, Integer, Double, Decimal 等,会被直接当做 XML 的节点内容,而自定义类型,会被按照对象树进行 XML 的转换。接下来,我们就这两种类型,来了解 JAXB 支持的 List 集合处理。

1、采用默认配置处理 List 集合
JABX默认支持 List 对象的解析,无需特别声明即可生成 List 对象的 XML。
处理基础数据类型的 List 集合
商品信息中的有很多小项,所以使用List类型。

@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
    @XmlAttribute
    private String id;
    private List<String> item;
	//  setters,getters
}

测试一下。

@Test 
public void test1() throws JAXBException {
    Product product = new Product();
    product.setId("1301");
    product.setItem(Arrays.asList("ItemA","ItemB","ItemC"));
    JAXB.marshal(product, System.out);
}

XML结果。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product id="1301">
    <item>ItemA</item>
    <item>ItemB</item>
    <item>ItemC</item>
</product>

这是最普通的一种转化方式。如果需要改变XML的Element的名称,可以设置@XmlElement(name = “Item”)。
处理类对象的 List 集合

@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
    @XmlAttribute
    private String id;
    private List<Item> item;
	//  setters,getters
}

每一个小项都更加复杂。

@XmlAccessorType(XmlAccessType.FIELD)
public class Item {
    @XmlAttribute
    private String id;
	//  setters,getters
}

测试一下。

@Test
public void test() throws JAXBException {
    Product product = new Product();
    product.setId("1303");
    product.setItem(Arrays.asList(
                new Item("13031", "ItemA"),
                new Item("13032", "ItemB"),
                new Item("13033", "ItemC")));
    JAXB.marshal(product, System.out);
}

XML结果。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product id="1303">
    <item id="13031">
        <name>ItemA</name>
    </item>
    <item id="13032">
        <name>ItemB</name>
    </item>
    <item id="13033">
        <name>ItemC</name>
    </item>
</product>

2、采用 @XmlElementWrapper 注解
处理基础数据类型的 List 集合
如果想让生成的XML外围被包裹起来,可以加上注解 @XmlElementWrapper。

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
    @XmlAttribute
    private String id;
    @XmlElementWrapper(name = "Items")
    private List<String> item;
	//  setters,getters
}

测试一下。

@Test
public void test() throws JAXBException {
    Product product = new Product();
    product.setId("1302");
    product.setItem(Arrays.asList("ItemA","ItemB","ItemC"));
    JAXB.marshal(product, System.out);
}

XML结果。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product2 id="1302">
    <Items>
        <item>ItemA</item>
        <item>ItemB</item>
        <item>ItemC</item>
    </Items>
</product2>

可以看到,item 有了父标签Items

处理类对象的 List 集合

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
    @XmlAttribute
    private String id;
    @XmlElementWrapper(name = "Items")
    private List<Item> item;
	//  setters,getters
}
@XmlAccessorType(XmlAccessType.FIELD)
public class Item {
    @XmlAttribute
    private String id;
	//  setters,getters
}

测试一下。

@Test
public void test() throws JAXBException {
    Product product = new Product();
    product.setId("1302");
    product.setItem(Arrays.asList(
                new Item("13031", "ItemA"),
                new Item("13032", "ItemB"),
                new Item("13033", "ItemC")));
    JAXB.marshal(product, System.out);
}

XML结果。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product id="1302">
    <Items>
	    <item id="13031">
	        <name>ItemA</name>
	    </item>
	    <item id="13032">
	        <name>ItemB</name>
	    </item>
	    <item id="13033">
	        <name>ItemC</name>
	    </item>
    </Items>
</product>

可以看到,item 有了父标签Items

3、采用 @XmlValue 注解,将非简单 Java 类转换为简单 XML 元素;
处理基础数据类型的 List 集合
注解 @XmlValue 只用于类对象。

处理类对象的 List 集合
商品信息中的小项还含有属性。

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
    @XmlAttribute
    private String id;
    private List<Item> item;
//  setters,getters
}

每一个小项都更加复杂,注意这里的 name 使用的注解@XmlValue。

@XmlAccessorType(XmlAccessType.FIELD)
public class Item {
    @XmlAttribute
    private String id;
    @XmlValue
    private String name;
//  setters,getters
}

测试一下。

@Test
public void test3() throws JAXBException {
    Product product = new Product();
    product.setId("1303");
    product.setItem(Arrays.asList(new Item("13031","ItemA"),
							    new Item("13032","ItemB"),
							    new Item("13033","ItemC")));
    JAXB.marshal(product, System.out);
}

生成的XML。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product id="1303">
    <item id="13031">ItemA</item>
    <item id="13032">ItemB</item>
    <item id="13033">ItemC</item>
</product>

通过 @XmlValue 注解,将类对象的某一字段/属性标识为节点内容。

4、采用 @XmlList 注解,在一个 XML 的 Element 中添加多个值
处理基础数据类型的 List 集合
在JAXB 中,有一个注解 @XmlList主要是为了在一个XML的Element中添加多个值。

@XmlAccessorType(XmlAccessType.FIELD)
public class Product4 {
    @XmlAttribute
    private String id;
    @XmlList
    private List<String> item;
//  setters,getters
}

测试一下。

@Test
public void test4() throws JAXBException {
    Product4 product = new Product4();
    product.setId("1304");
    product.setItem(Arrays.asList("ItemA","ItemB","ItemC"));
    JAXB.marshal(product, System.out);
}

生成的XMl 如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product4 id="1304">
    <item>ItemA ItemB ItemC</item>
</product4>

可以看到,item包含了List中的所有数据。

处理类对象的 List 集合
注解 @XmlList 与类对象不兼容。

4.4 一对多(Map)关联 Java 类与 XML 的转换

Map节点的名称变更
改造需要的代码量比较多,因为JAXB原生不指定Map的自定义操作。也可以说JAXB不支持Map这种数据类型。所以需要使用到适配器来扩展 JAXB 的功能。

首先定义一个类,其中只有两个字段,为了简单,可以不写setters/getters方法。通过这种方式模拟一个Map,只包含key/value,也就是first/second,这个名称就是XML的节点显示名称。

public class XmlMap {
    public String first;
    public String second;
}

自定义一个Adapter,这里将所有的代码都展示出来。

public class MapAdapter extends XmlAdapter<XmlMap[], Map<String, String>>{
    @Override
    public Map<String, String> unmarshal(XmlMap[] v) throws Exception {
        Map<String, String> map = new HashMap<>();
        for(int i=0; i<v.length; i++) {
            XmlMap pairs = v[i];
            map.put(pairs.first, pairs.second);
        }
        return map;
    }
    @Override
    public XmlMap[] marshal(Map<String, String> v) throws Exception {
        XmlMap[] xmlMap = new XmlMap[v.size()];
        int index = 0;
        for(Map.Entry<String, String> entry: v.entrySet()) {
            XmlMap xm = new XmlMap();
            xm.first = entry.getKey();
            xm.second = entry.getValue();
            xmlMap[index++] = xm;
        }
        return xmlMap;
    }
}

@XmlJavaTypeAdapterJAXB能够内置支持List和Set集合,但是对于Map的支持需要自己处理。它继承自抽象类XmlAdapter<ValueType,BoundType> 类型参数:

  • BoundType JAXB 不知道如何处理的一些类型。自定义的类型,告诉Jaxb ValueType 将此类型用作内存表示形式。
  • ValueType JAXB 无需其他操作便知道如何处理的类型。
    这里的Map对于JAXB是一个未知类型,但是XmlMap[]却是已知的对象数组类型。通过中间的转化赋值,可以使XmlMap[]与Map相互转化,从而让Jaxb知道数据如何处理。

在之前的Product中,在Map上加上注解@XmlJavaTypeAdapter(MapAdapter.class)。

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Product2 {
    private String id;
    @XmlJavaTypeAdapter(MapAdapter.class)
    public Map<String, String> category;
//  setters, getters
}

测试一下:

@Test 
public void test2() throws JAXBException {
    Map<String, String> map = new HashMap<>();
    map.put("衣服", "大衣");
    map.put("裤子", "西裤");
    Product2 product = new Product2();
    product.setId("1402");
    product.setCategory(map);
    JAXB.marshal(product, System.out);
}

得到的结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<product2>
    <id>1402</id>
    <category>
        <item>
            <first>衣服</first>
            <second>大衣</second>
        </item>
        <item>
            <first>裤子</first>
            <second>西裤</second>
        </item>
    </category>
</product2>

上面的所有节点名称除了item都是可以通过一定的方法改变的。


上一章:JAXB-3 JAXB API
目录:学习 JAXB
下一章:JAXB-5 动态 XML 生成


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值