「Java 开发工具 · 建议收藏」详细讲解:Xstream 对象转 XML工具
一、介绍
XStream 是一个简单的基于 Java 库,Java 对象序列化到 XML,反之亦然 (即:可以轻易的将 Java 对象和 xml 文档相互转换)。
特点:
- 使用方便 - XStream 的 API 提供了一个高层次外观,以简化常用的用例。
- 无需创建映射 - XStream 的 API 提供了默认的映射大部分对象序列化。
- 性能 - XStream 快速和低内存占用,适合于大对象图或系统。
- 干净的 XML - XStream 创建一个干净和紧凑 XML 结果,这很容易阅读。
- 不需要修改对象 - XStream 可序列化的内部字段,如私有和最终字段,支持非公有制和内部类。默认构造函数不是强制性的要求。
- 完整对象图支持 - XStream 允许保持在对象模型中遇到的重复引用,并支持循环引用。
- 可自定义的转换策略 - 定制策略可以允许特定类型的定制被表示为 XML 的注册。
- 安全框架 - XStream 提供了一个公平控制有关解组的类型,以防止操纵输入安全问题。
- 错误消息 - 出现异常是由于格式不正确的 XML 时,XStream 抛出一个统一的例外,提供了详细的诊断,以解决这个问题。
- 另一种输出格式 - XStream 支持其它的输出格式,如 JSON。
二、使用案例
- JDK 环境:JDK8,其他版本的JDK6+
- maven:3.8.6
- Xstream:1.4.20
- 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:① 在对象上添加别名注解;② 使用时开启注解和应用注解;
- 方法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博客
附录
- 官网说明:http://x-stream.github.io/
- 官网使用指南直达车:XStream - Two Minute Tutorial
- GitHub:https://github.com/x-stream/xstream
- JavaDoc:http://x-stream.github.io/javadoc/index.html
- Maven 版本快速查找网站:Maven搜索-最快捷的Maven搜索-由源码阅读网提供技术服务
- 友情博客连接:XStream反序列化漏洞原理分析 - 郑瀚Andrew - 博客园
- 友情博客链接:xStream转换XML、JSON - nevergiveupzeng - 博客园