1. 概述

本教程我们将学习如何使用 Jackson 2.x 将Java对象序列化为XML格式数据,以及如何反序列化。

我们主要关注一些基础用法,不涉及太复杂或自定义操作。

2. XmlMapper 对象

Jackson 2.x 中主要用到 XmlMapper 类来序列化,所以我们需要创建一个它的实例:

XmlMapper mapper = new XmlMapper();

别忘了添加 maven 依赖

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.11.1</version>
</dependency>

获取最新版本号请检查maven中央仓库

3. 将Java对象序列化为XML

XmlMapperObjectMapper(用于JSON序列化)的子类,添加了一些关于XML的改动。

下面我们看看如何使用它来进行序列化。首先创建我们的Java类:

    class SimpleBean {
        private int x = 1;
        private int y = 2;
        
        //standard setters and getters
    }

3.1. 序列化为XML字符串

将Java对象序列化为XML字符串:

@Test
public void whenJavaSerializedToXmlStr_thenCorrect() throws JsonProcessingException {
    XmlMapper xmlMapper = new XmlMapper();
    String xml = xmlMapper.writeValueAsString(new SimpleBean());
    assertNotNull(xml);
}

结果:

<SimpleBean>
    <x>1</x>
    <y>2</y>
</SimpleBean>

3.2. 序列化为XML文件

也可以将序列化结果输出到XML文件中:

@Test
public void whenJavaSerializedToXmlFile_thenCorrect() throws IOException {
    XmlMapper xmlMapper = new XmlMapper();
    xmlMapper.writeValue(new File("simple_bean.xml"), new SimpleBean());
    File file = new File("simple_bean.xml");
    assertNotNull(file);
}

simple_bean.xml文件中的内容:

<SimpleBean>
    <x>1</x>
    <y>2</y>
</SimpleBean>

4. 反序列化为Java对象

本节,我们将看到如何将XML反序列化为Java对象。

4.1. 反序列化XML字符串

与序列化一样,我们也可以将 XML 字符串反序列化回 Java 对象:

@Test
public void whenJavaGotFromXmlStr_thenCorrect() throws IOException {
    XmlMapper xmlMapper = new XmlMapper();
    SimpleBean value
      = xmlMapper.readValue("<SimpleBean><x>1</x><y>2</y></SimpleBean>", SimpleBean.class);
    assertTrue(value.getX() == 1 && value.getY() == 2);
}

4.2. 反序列化XML文件

首先我们要读取文件内容,将字节流转为字符串。然后剩下的代码和4.1节一样:

@Test
public void whenJavaGotFromXmlFile_thenCorrect() throws IOException {
    File file = new File("simple_bean.xml");
    XmlMapper xmlMapper = new XmlMapper();
    String xml = inputStreamToString(new FileInputStream(file));
    SimpleBean value = xmlMapper.readValue(xml, SimpleBean.class);
    assertTrue(value.getX() == 1 && value.getY() == 2);
}

inputStreamToString 方法实现:

public String inputStreamToString(InputStream is) throws IOException {
    StringBuilder sb = new StringBuilder();
    String line;
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    while ((line = br.readLine()) != null) {
        sb.append(line);
    }
    br.close();
    return sb.toString();
}

5. 处理大写元素

在本节中,我们将研究如何处理我们需要反序列化带有大写元素的 XML 或需要将 Java 对象序列化为带有一个或多个大写元素的 XML 的场景。

5.1. 反序列化XML字符串

假设我们有一个 XML,其中一个字段大写

<SimpleBeanForCapitalizedFields>
    <X>1</X>
    <y>2</y>
</SimpleBeanForCapitalizedFields>

为了正确处理大写元素,我们需要使用@JsonProperty 来注解“x”字段:

    class SimpleBeanForCapitalizedFields {
        @JsonProperty("X")
        private int x = 1;
        private int y = 2;
    
        // standard getters, setters
    }

现在我们可以正确的将XML字符串解析回Java对象:

    @Test
    public void whenJavaGotFromXmlStrWithCapitalElem_thenCorrect() throws IOException {
        XmlMapper xmlMapper = new XmlMapper();
        SimpleBeanForCapitalizedFields value
          = xmlMapper.readValue(
          "<SimpleBeanForCapitalizedFields><X>1</X><y>2</y></SimpleBeanForCapitalizedFields>",
          SimpleBeanForCapitalizedFields.class);
        assertTrue(value.getX() == 1 && value.getY() == 2);
    }

5.2. 序列化为XML字符串

通过使用@JsonProperty注解,我们可以正确地将 Java 对象序列化为具有一个或多个大写元素的 XML 字符串:

@Test
public void whenJavaSerializedToXmlFileWithCapitalizedField_thenCorrect()
  throws IOException {
    XmlMapper xmlMapper = new XmlMapper();
    xmlMapper.writeValue(new File("target/simple_bean_capitalized.xml"),
      new SimpleBeanForCapitalizedFields());
    File file = new File("target/simple_bean_capitalized.xml");
    assertNotNull(file);
}

6. 将List序列化为XML

XmlMapper 能够将整个 Java bean 序列化为一个XML文档。下面我们演示一个带有嵌套对象和数组的例子。

我们目标是将Person对象,包括组合对象Address序列化为XML。

我们最终的XML大概是这样的:

<Person>
    <firstName>Rohan</firstName>
    <lastName>Daye</lastName>
    <phoneNumbers>
        <phoneNumbers>9911034731</phoneNumbers>
        <phoneNumbers>9911033478</phoneNumbers>
    </phoneNumbers>
    <address>
        <streetName>Name1</streetName>
        <city>City1</city>
    </address>
    <address>
        <streetName>Name2</streetName>
        <city>City2</city>
    </address>
</Person>

注意看,我们的电话号码封装在phoneNumbers包装器元素中,而address没有。

    public final class Person {
        private String firstName;
        private String lastName;
        private List<String> phoneNumbers = new ArrayList<>();
        @JacksonXmlElementWrapper(useWrapping = false)
        private List<Address> address = new ArrayList<>();
    
        //standard setters and getters
    }

我们可以使用@JacksonXmlElementWrapper(localName = ‘phoneNumbers')指定包装器元素的名字。如果不想包装我们的元素,可以通过@JacksonXmlElementWrapper(useWrapping = false)禁用掉。

下面是Address类的定义:

public class Address {
    String streetName;
    String city;
    //standard setters and getters
}

其余的Jackson替我们处理。现在我们可以像前面例子一样,再次调用 writeValue

    private static final String XML = "<Person>...</Person>";

    @Test
    public void whenJavaSerializedToXmlFile_thenSuccess() throws IOException {
        XmlMapper xmlMapper = new XmlMapper();
        Person person = testPerson(); // test data
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        xmlMapper.writeValue(byteArrayOutputStream, person); 
        assertEquals(XML, byteArrayOutputStream.toString()); 
    }

7. 将XML反序列化为List

同样,Jackson 也可以解析包含对象集合的 XML。

使用上面的数据测试:

@Test
public void whenJavaDeserializedFromXmlFile_thenCorrect() throws IOException {
    XmlMapper xmlMapper = new XmlMapper();
    Person value = xmlMapper.readValue(XML, Person.class);
    assertEquals("City1", value.getAddress().get(0).getCity());
    assertEquals("City2", value.getAddress().get(1).getCity());
}

8. 总结

本文我们演示了如何使 Jackson将Java对象序列化为XML,以及如何反序列化。

惯例,本文示例程序源码托管在GitHub上。