「Java 开发工具 · 建议收藏」详细讲解:Xstream 对象转 XML工具

一、介绍

XStream 是一个简单的基于 Java 库,Java 对象序列化到 XML,反之亦然 (即:可以轻易的将 Java 对象和 xml 文档相互转换)。

特点

  1. 使用方便 - XStream 的 API 提供了一个高层次外观,以简化常用的用例。
  2. 无需创建映射 - XStream 的 API 提供了默认的映射大部分对象序列化。
  3. 性能 - XStream 快速和低内存占用,适合于大对象图或系统。
  4. 干净的 XML - XStream 创建一个干净和紧凑 XML 结果,这很容易阅读。
  5. 不需要修改对象 - XStream 可序列化的内部字段,如私有和最终字段,支持非公有制和内部类。默认构造函数不是强制性的要求。
  6. 完整对象图支持 - XStream 允许保持在对象模型中遇到的重复引用,并支持循环引用。
  7. 可自定义的转换策略 - 定制策略可以允许特定类型的定制被表示为 XML 的注册。
  8. 安全框架 - XStream 提供了一个公平控制有关解组的类型,以防止操纵输入安全问题。
  9. 错误消息 - 出现异常是由于格式不正确的 XML 时,XStream 抛出一个统一的例外,提供了详细的诊断,以解决这个问题。
  10. 另一种输出格式 - XStream 支持其它的输出格式,如 JSON

二、使用案例

  1. JDK 环境:JDK8,其他版本的JDK6+
  2. maven:3.8.6
  3. Xstream:1.4.20
  4. maven 版本快速查找网站:Maven搜索-最快捷的Maven搜索-由源码阅读网提供技术服务

(1)引入 maven 依赖

提醒:本篇文章使用到的依赖,请确保依赖存在再进行亲自体验测试;

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- 核心依赖 xstream -->
<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.20</version>
</dependency>
<!-- 核心依赖 xstream 在处理 XML 转 JSON 的时候需要此依赖,否则会出现找不到JSON 映射类 -->
<dependency>
    <groupId>org.codehaus.jettison</groupId>
    <artifactId>jettison</artifactId>
    <version>1.5.4</version>
</dependency>

<!-- Jackson:如果是 SpringBoot 框架,则无需引入! -->  
<dependency>  
    <groupId>com.fasterxml.jackson.core</groupId>  
    <artifactId>jackson-databind</artifactId>  
    <version>2.13.0</version>  
</dependency>

(2)对象转 XML:基础简单使用

① 控制层直接输出 XML 格式数据

@RestController
@RequestMapping("/demo")
public class DemoController {

    @RequestMapping(value = "/hello" ,produces = {"application/xml;"})
    public String showHelloWorld(){
        Employee e1 = Employee.builder().firstName("张").lastName("三").age(18).salary(10000).gender("Male").build();
        // Serializing a Java object into XML
        XStream xStream = new XStream(new DomDriver());
        String xml = xStream.toXML(e1); // Converting it to XML
        return xml;
    }
}

演示结果:

(3)对象转 XML:三种方式

public static void main(String[] args) {
    Employee employee = Employee.builder().firstName("张").lastName("三").age(18).salary(10000).gender("Male").build();

    //XML序列化方式一:需要XPP3库
    XStream xstream_1 = new XStream();
    String xml_1 = xstream_1.toXML(employee);
    System.out.println("xml_1: (需要XPP3库)\n" + xml_1);

    // XML序列化方式二:不需要XPP3库
    XStream xstream_2 = new XStream(new DomDriver());
    String xml_2 = xstream_2.toXML(employee);
    System.out.println("xml_2: (不需要XPP3库)\n" + xml_2);

    // XML序列化方式三:不需要XPP3库开始使用Java6
    XStream xstream_3 = new XStream(new StaxDriver());
    xstream_3.alias("员工",Employee.class);//为类名节点重命名
    String xml_3 = xstream_3.toXML(employee);
    System.out.println("xml_3:(不需要XPP3库开始使用Java6) \n" + xml_3);

}

输出结果:

xml_1: (不需要XPP3库)
<com.example.demo.controller.entity.Employee>
  <firstName>张</firstName>
  <lastName>三</lastName>
  <salary>10000</salary>
  <age>18</age>
  <gender>Male</gender>
</com.example.demo.controller.entity.Employee>

xml_2: (需要XPP3库)
<com.example.demo.controller.entity.Employee>
  <firstName>张</firstName>
  <lastName>三</lastName>
  <salary>10000</salary>
  <age>18</age>
  <gender>Male</gender>
</com.example.demo.controller.entity.Employee>

xml_3:(不需要XPP3库开始使用Java6) 
<?xml version="1.0" ?><员工><firstName>张</firstName><lastName>三</lastName><salary>10000</salary><age>18</age><gender>Male</gender></员工>

(3)对象转 XML:重命名节点名

官方直达示例教程:XStream - 别名教程 (x-stream.github.io)

  1. 两种方法:
    1. 方法1:① 在对象上添加别名注解;② 使用时开启注解和应用注解;
    2. 方法2:无需再对象上添加对应的类/属性别名注解,直接再使用时 alias() / aliasField() 即可;

【1】对象属性标记注解

@Data
@Builder
@XStreamAlias("员工")
public class Employee {
    @XStreamAlias("名")
    public String firstName;
    @XStreamAlias("姓")
    public String lastName;
    @XStreamAlias("薪资")
    public int salary;
    @XStreamAlias("年龄")
    public int age;
    @XStreamAlias("性别")
    public String gender;
}

【2】开启注解识别

public static void beanToXmlAlias() {
    Employee employee = Employee.builder().firstName("张").lastName("三").age(18).salary(10000).gender("Male").build();

    //XML序列化方式:需要XPP3库
    XStream xstream_1 = new XStream();
    xstream_1.processAnnotations(Employee.class);// 应用Employee类的注解
    xstream_1.autodetectAnnotations(true);// 自动检测注解
    String xml_1 = xstream_1.toXML(employee);
    System.out.println("xml_1: (需要XPP3库)\n" + xml_1);
}

【3】结果展示

【4】扩展:另一种设置别名的方式

public static void beanToXmlAlias2() {
    Person person = Person.builder().id(1).name("King").build();
    XStream xstream_1 = new XStream();

    // 1.设置类别名
    xstream_1.alias("person", Person.class);
    // 2.设置属性别名
    xstream_1.aliasField("personId", Person.class, "id");// 将属性id设置别名为 personId
    xstream_1.aliasField("personName", Person.class, "name");// 将属性name设置别名为 personName
    // 3.其他 还可以设置包别名等

    String xml_1 = xstream_1.toXML(person);
    System.out.println("xml_1: (需要XPP3库)\n" + xml_1);
}

结果展示:


(4)XML格式转对象:基础使用

【1】定义反序列化对象

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Person {
    private Integer id;
    private String name;
}

【2】简单使用示例

public static void xmlToBean() {
    Person bean = Person.builder().id(1).name("Drew").build();
    XStream xstream = new XStream(new DomDriver());//设置Json解析器
    //Json序列化
    String xml = xstream.toXML(bean);
    System.out.println(xml);

    //【重点】Json反序列化(注意:需要设置可访问的类对象机制)
    xstream.allowTypes(new Class[]{Person.class});
    bean = (Person) xstream.fromXML(xml);
    System.out.println(bean);
}

【3】结果展示


【踩坑记录】反序列化的时候出现Exception in thread "main" com.thoughtworks.xstream.security.ForbiddenClassException

解决方案:

XStream xstream = new XStream();  
// 允许反序列化对应的类, 如果Person属性是一个类对象,也需要在这里面包含,否则依旧会出现此问题!
xstream.allowTypes(new Class[]{Person.class});

请注意,在使用 XStream 时,特别是当处理来自不受信任的源的数据时,要格外小心,以确保你的应用不会被恶意输入所利用。

*(5)XML 转 对象:增加别名使用示例

【1】对象属性标记注解

对反序列化的对象的属性进行标记别名,使用注解 @XStreamAlias 即可。

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
@XStreamAlias("人")
public class Person {
    @XStreamAlias("编号")
    private Integer id;
    @XStreamAlias("名称")
    private String name;
}

【2】使用示例

别名替换了节点,不会被反序列化的时候识别的,请在传入的 XML文件中使用对应接受的DTO属性名保持一致。

【3】结果展示

无法识别,不展示!

(6)xstream框架将 对象转JSON :简单使用示例

【1】定义对象

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Person {
    private Integer id;
    private String name;
}

【2】简单使用

public static void xmlToJson() {
    Person person = new Person(1, "Banana");
    XStream xstream = new XStream(new JettisonMappedXmlDriver());
    xstream.setMode(XStream.NO_REFERENCES);
    xstream.alias("person", Person.class);
	// 输出: object → json : {"person":{"id":1,"name":"Banana"}}
    System.out.println(" object → json : " + xstream.toXML(person));

    xstream.aliasField("personId", Person.class, "id");
    xstream.aliasField("personName", Person.class, "name");
    // 输出: object → json alias : {"person":{"personId":1,"personName":"Banana"}}
    System.out.println(" object → json alias : " + xstream.toXML(person));
}

【踩坑记录】出现异常 Exception in thread "main" java.lang.NoClassDefFoundError: org/codehaus/jettison/mapped/Configuration

解决方案:引入 maven 依赖:即可。

<dependency>
    <groupId>org.codehaus.jettison</groupId>
    <artifactId>jettison</artifactId>
    <version>1.5.4</version>
</dependency>

(7)xstream 的 XML 转 JSON

将 xml 转成JSON 则需要 xstream 框架兼容其他的JSON工具依赖,使用第三方的转换驱动进行替换。

【1】引入maven依赖:jackson

注意:如果是 Spring Boot 框架则无需引入 Jackson 框架(Spring Boot 内置了 Jackson 依赖)

<dependencies>  
    <!-- XStream -->  
    <dependency>  
        <groupId>com.thoughtworks.xstream</groupId>  
        <artifactId>xstream</artifactId>  
        <version>1.4.18</version>  
    </dependency>  

    <!-- Jackson -->  
    <dependency>  
        <groupId>com.fasterxml.jackson.core</groupId>  
        <artifactId>jackson-databind</artifactId>  
        <version>2.13.0</version>  
    </dependency>  
</dependencies>

【2】使用演示

public static void xmlToJson() throws JsonProcessingException {
    // XML 字符串
    String xml = "<person><name>John Doe</name><id>30</id></person>";

    // 1.使用 XStream 将 XML 反序列化为 Java 对象
    XStream xstream = new XStream();
    xstream.allowTypes(new Class[]{Person.class});// 注意1
    xstream.alias("person", Person.class); // 注意2:需要和XML文件的根节点一致
    Person person = (Person) xstream.fromXML(xml);

    // 2.使用 Jackson 将 Java 对象转换为 JSON 字符串
    ObjectMapper objectMapper = new ObjectMapper();
    String json = objectMapper.writeValueAsString(person);

    // 3.输出 JSON 字符串
    System.out.println(json);
}

测试结果:

扩展:更复杂的对象(map/list/嵌套属性的对象)转换请看友情链接:xStream转换XML、JSON - nevergiveupzeng - 博客园 (cnblogs.com)

(8)XStream 自定义转换器

官方直达示例教程地址:XStream - Converter Tutorial (x-stream.github.io)

【1】定义示例对象

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Person {
    private Integer id;
    private String name;
}

【2】定义自定义转换器

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class PersonConverter implements Converter {
    //定义转换器能转换的JavaBean类型
    @Override
    public boolean canConvert(Class type) {
        return type.equals(Person.class);
    }

    //把对象序列化成XML或Json
    @Override
    public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
        Person person = (Person) value;
        writer.startNode("编号");
        writer.setValue(person.getId() + "");
        writer.endNode();
        writer.startNode("姓名");
        writer.setValue(person.getName());
        writer.endNode();
        writer.startNode("转换器");
        writer.setValue("自定义的转换器");
        writer.endNode();
    }

    //把XML或Json反序列化成对象
    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        Person person = new Person(1, "Bob");
        // 第一个属性
        reader.moveDown();
        person.setId(Integer.parseInt(reader.getValue()));
        reader.moveUp();
        // 第二个属性
        reader.moveDown();
        person.setName(reader.getValue());
        reader.moveUp();

        return person;
    }
}

【3】测试演示

public static void xmlConvertor() {
    Person bean = new Person(19, "张三");
    XStream xstream = new XStream();
    xstream.registerConverter(new PersonConverter());//注册转换器
    //序列化
    String xml = xstream.toXML(bean);
    System.out.println(xml);
    //反序列化
    xstream.allowTypes(new Class[]{Person.class});
    bean = (Person)xstream.fromXML(xml);
    System.out.println(bean);
}

结果展示:

(9)Xstream 对象流使用方法

使用实例:这里重点展示 输出流的用法。

官方直达示例教程:XStream - Object Streams Tutorial

public static void xstreamStream() throws IOException {
    XStream xstream = new XStream();
    ObjectOutputStream out = xstream.createObjectOutputStream(System.out);
    out.writeObject(new Person(1, "张三"));
    out.writeObject(new Person(2, "李四"));
    out.writeObject("Hello");
    out.writeInt(12345);
    out.close();
}

结果输出:

<object-stream>
  <com.example.demo.controller.util.Person>
    <id>1</id>
    <name>张三</name>
  </com.example.demo.controller.util.Person>
  <com.example.demo.controller.util.Person>
    <id>2</id>
    <name>李四</name>
  </com.example.demo.controller.util.Person>
  <string>Hello</string>
  <int>12345</int>
</object-stream>

(10)Xstream 数据持久化

使用实例如下:① 创建一个 XStream 对象,设置允许访问转化的对象;② 添加对应的XML文件的数据;

官方直达教程示例:XStream - Persistence API Tutorial

public static void xstreamStrategy() {
    XStream xstream = new XStream();
    // 注意:xstream 1.4.20 需要手动允许转换的类型,否则禁止访问Person对象属性
    xstream.allowTypes(new Class[]{Person.class});
    PersistenceStrategy strategy = new FilePersistenceStrategy(new File("./target"), xstream);
    XmlArrayList xmlArrayList = new XmlArrayList(strategy);
    xmlArrayList.add(0, new Person(1, "Bob"));
    xmlArrayList.add(1, new Person(2, "King"));
    xmlArrayList.add(2, new Person(3, "Baby"));
    System.out.println(xmlArrayList);// 此行可以省略(如果无需输出显示)
}

结果展示:

【重要提醒】漏洞修复

xstream 版本在 1.4.11 和 1.4.16 版本的漏洞,任意删除一个文件,请尽快升级到 Xstream 的最新版本(目前版本为 1.4.20)即可。漏洞复现和研究参考地址:xstream 反序列化漏洞研究与修复_xstream不安全的序列号-CSDN博客


附录

相关文章

Java基础之String与int两者之间如何相互转换?

项目开发中String字符串和int整型之间的转换操作是很常见的,当然可能你也会遇到String字符串和其它基本数据类型的转换操作,比如float、long、double等常见的类型。那么如果我们学会...

Java Jackson 中如何将 JSON 对象转换为字符串

通常来说只需要下面的 1 行代码就可以完成下面的转换了。请考察下面的代码:String newsletterJSON = mapper.writerWithDefaultPrettyPrinter()...

Java Array 和 String 的转换

英文标题【Array to String Conversions】概述本页面中的内容对 Array 和 String 之间互相进行转换的方法进行一些说明。我们可以使用 原生 Java(vanilla...

JAVA快速入门——字符和字符串

字符尽管字符和字符串名字相似;但在JAVA中是两个不同的类型;字符类型char是基本数据类型,(character的缩写)。一个char保存一个Unicode字符:char oneChar = 'A'...

Java对象与JSON字符串互相转换

Java对象与JSON字符串互相转换1.准备工作在与前端页面交互时,经常需要传递json字符串数据,我们可以使用JSONObject来处理json字符串。需要导入2个jar包:fastjson-1.2...

java学习分享:Java截取(提取)子字符串(substring())

在 String 中提供了两个截取字符串的方法,一个是从指定位置截取到字符串结尾,另一个是截取指定范围的内容。下面对这两种方法分别进行介绍。1. substring(int beginIndex) 形...