xml序列化框架使用对比(Jaxb,XStream,Jackson)

发布时间 2023-05-24 22:01:28作者: wusanga

一、Jaxb

1.1 介绍

JAXB(Java Architecture for XML Binding简称JAXB)允许Java开发人员将Java类映射为XML表示方式。JAXB提供两种主要特性:将一个Java对象序列化为XML,以及反向操作,将XML解析成Java对象。

常用注解:

  • @XmlRootElement:定义Xml根元素,序列化的根类必须有该注解
  • @XmlElement:将JavaBean属性映射到对应的xml元素
  • @XmlAttribute:将JavaBean属性映射到XML属性
  • @XmlElementWrapper:根据集合来产生包装元素。它必须与collection属性一起使用
@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {

  @XmlElementWrapper
  @XmlElement(name="hobby")
  List<String> hobbies;
}

生成的xml如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
  <hobbies>
    <hobby>Swimming</hobby>
    <hobby>basketball</hobby>
  </hobbies>
</employee>
  • @XmlAccessorType:使用Java类中的哪些字段或属性来生成xml。它有四个选项,默认是PUBLIC_MEMBER
    • NONE:除非使用某些JAXB注释专门对其进行注释,否则所有字段或属性均不会被映射
    • FIELD:字段映射
    • PROPERTY:getter/setter对映射
    • PUBLIC_MEMBER:public的getter/setter对以及public字段映射
  • @XmlAccessorOrder:控制类中字段和属性的顺序。有ALPHABETICAL(字母顺)和UNDEFINED(类中字段顺序)两个选择。只对字段映射生效
  • @XmlTransient:不做序列化
  • @XmlJavaTypeAdapter:用于自定义Xml和Java属性的转换器

1.2 使用方式

  1. 序列化
UserDO user = createUser();

JAXBContext jaxbContext = JAXBContext.newInstance(UserDO.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

StringWriter sw = new StringWriter();
marshaller.marshal(user, sw);

String xml = sw.toString();
System.out.println(xml);
  1. 返序列化
JAXBContext jaxbContext = JAXBContext.newInstance(UserDO.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
UserDO unSerialUser = (UserDO) unmarshaller.unmarshal(new StringReader(xml));
System.out.println(unSerialUser);

1.3 总结

  • 要序列化的根类必须加上@XmlRootElement注解(不加也有方法实现,但是很麻烦)
  • JAXB默认序列化getter和setter,且必须成对出现才会被序列化。建议指定使用Field序列化方式
  • 默认null值不会被序列化,如果需要序列化需要增加@XmlElement(nillable = true)
  • 空标签<age></age>的返序列化:如果age为Integer类型,则反序列化后的age=0;如果age是Long类型,则age=null;如果age为String类型,则age=""
  • JAXBContext是线程安全的,但Marshaller和Unmarshaller是非线程安全的,可以考虑池化

二、XStream

2.1 介绍

官网:http://x-stream.github.io/index.html
XStream是一个简单的基于Java的类库,用来将Java对象序列化成XML或反序列化为对象。XStream可以通过编程或注解的方式,进行xml的转换。
一个XStream实例在其生命周期中设想了两个阶段:

  • 设置:当XStream实例被实例化时,很多默认的配置已经被应用,也就是说,实例处于设置阶段。现在是应用进一步配置的时候了。这个阶段不是线程安全的
  • 执行:一旦实例被正确配置,就可以在执行阶段使用,执行阶段是线程安全的,也就是说,有可能同时使用一个XStream实例来执行。因此,在执行阶段之后不应该重新配置一个实例

XStream的结构由六个主要部件组成:

  • Converters:转换器用于对象和xml互转,自定义转换器可以实现Converter接口,然后通过xStream.registerConverter方法进行注册
  • Mappers:映射器用于xml名称和java名称之间的互转,可以覆盖XStream的wrapMapper函数,添加自定义映射器实现
  • Drivers (Writer and Reader):底层对xml读写的驱动,避免将XStream捆绑在一个特定的库上
  • Context:序列化或反序列化时,会创建Context,用于数据遍历,然后委托给Converter
  • Type Permissions:安全框架的一部分。这些实现是用来拒绝或允许根据java类型的名称或类型层次来进行反序列化的
  • Facade:XStream为门面类,通常作为入口点

常用注解

  • @XStreamAlias:定义别名
  • @XStreamAsAttribute:把字段设置为属性
  • @XStreamImplicit:省略集合根节点,和JAXB的@XmlElementWrapper相反
  • @XStreamOmitField:隐藏字段
  • @XStreamConverter:设置该字段使用自定义转换器

2.2 使用方式

  1. 引入依赖
<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.20</version>
</dependency>
  1. 配置权限,设置最低权限,或者指定类(这种方式需要涉及到的所有类,如果用到了泛型,需要把实际类型也加进来)
XStream xStream = new XStream();
// 允许任何类型反序列化
xStream.addPermission(AnyTypePermission.ANY);
// 允许自定义类型反序列化(需要所有类)
xStream.allowTypes(new Class[]{UserDO.class, FavoriteDO.class});
  1. 序列化,使用编程方式
UserDO user = createUser();

XStream xStream = new XStream();
xStream.addPermission(AnyTypePermission.ANY);

// 定义别名,否则默认输出的根路径为类的全类名
xStream.alias("user", UserDO.class);
// aliasField修改java字段和xml字段属性映射
xStream.aliasField("username", UserDO.class, "name");
// 字段输出成xml的属性
xStream.useAttributeFor(UserDO.class, "name");
String xml = xStream.toXML(user);
  1. 反序列化,使用编程方式
XStream xStream = new XStream();
xStream.addPermission(AnyTypePermission.ANY);

xStream.alias("user", UserDO.class);

UserDO user = (UserDO)xStream.fromXML(xml);
System.out.println(user);

反序列化需要先用alias注册别名,因为fromXML没有类型参数,Xstream是通过xml的根节点识别哪个java类型
5. 序列化和反序列化,注解方式

XStream xStream = new XStream();
xStream.addPermission(AnyTypePermission.ANY);
// 相当于注册,否则直接反序列化不知道类型
xStream.processAnnotations(UserDO.class);

// 序列化
UserDO user = createUser();
String xml = xStream.toXML(user);
// 反序列化
UserDO user2 = (UserDO)xStream.fromXML(xml);

2.3 总结

  • XStream使用的是字段映射
  • XStream反序列化需要配置权限
  • XStream可以通过编程方式,或注解方式,进行序列化和反序列化配置。但不管哪种方式,都需要预先注册类(编程方式要注册更多类,注解方式只用注册根类)
  • 处理空标签时,比如<age></age>,默认是解析为"",如果该类型定义的是Integer之类的,会直接报错
  • 默认不认识的标签也会报错,需要配置:xStream.ignoreUnknownElements();
  • XStream在配置阶段是非线程安全,配置完成后执行时是线程安全的

三、Jackson

3.1 介绍

官网:https://github.com/FasterXML/jackson-dataformat-xml

jackson的xml模块目标是模仿JAXB数据绑定在 "代码优先 "方法下的工作方式,全面支持往返。支持使用jackson的注解,或者使用JAXB的注解(需要引入额外的包)。可以通过编程的方式手动读写xml元素(相当于读写dom节点一样,用的比较少,就不介绍了)

常用注解:

  • @JacksonXmlRootElement:定义Xml根元素,默认使用类的SimpleName
  • @JacksonXmlProperty:指定属性名称,以及属性是否被写成一个XML元素或属性
  • @JacksonXmlElementWrapper:允许指定用于包装List和Map属性的XML元素。类似JAXB的@XmlElementWrapper
  • @JacksonXmlCData:允许指定一个属性的值被序列化在一个CData标签中

常用的配置属性:

  • SerializationFeature.INDENT_OUTPUT:是否格式化输出,默认false
  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS:日期写为时间戳,默认true
  • MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES:反序列化忽略大小写,默认false
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES:反序列化时,未知字段是否直接失败,默认true

3.2 使用方式

  1. 引入依赖
<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-xml</artifactId>
  <version>2.15.0</version>
</dependency>
  1. 序列化和反序列化
UserDO user = createUser();

XmlMapper mapper = XmlMapper.builder()
      .enable(SerializationFeature.INDENT_OUTPUT)
      .build();

String xml = mapper.writeValueAsString(user);
System.out.println(xml);

UserDO user2 = mapper.readValue(xml, UserDO.class);
System.out.println(user2);

3.3 总结

  • jackson使用的getter/setter方法进行映射(需要成对出现),注解可以加在方法或者字段上,如果都加了会进行合并,但不能有冲突,推荐就加在字段上
  • 处理空标签的结果比较人性化,比如<age></age>,如果定义的是String,则为"";如果定义的是Integer,则为null
  • 默认不认识的标签也会报错,需要配置:mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
  • XmlMapper配置好之后,执行时是线程安全的

四、框架比较

框架 Jaxb XStream Jackson
实现方式 基于注解 编程或注解 基于注解
映射方式 默认get/set方法映射,可以指定Field 默认Field映射 默认get/set方法映射
线程安全性 JAXBContext是线程安全的,但Marshaller和Unmarshaller是非线程安全的 XStream配置完成后执行时是线程安全的 XmlMapper配置完成后执行时是线程安全的
空标签的处理 Integer会解析为0,Long会解析为null 默认解析为"",字段定义为数值类型会报错,需要自己再实现自定义转换 会根据类型自动判断(Integer为null, String为""
安全性 不用设置权限 有很多安全问题未解决,使用上也必须设置权限,不友好 不用设置权限
易用性 一般 一般

五、参考

https://zhuanlan.zhihu.com/p/343893930?utm_id=0
https://zhuanlan.zhihu.com/p/145849932