一、Jaxb

1.1 介绍

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

常用注解:

  • @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 使用方式

    主要使用的类有JAXBContext,Marshaller,Unmarshaller。JAXBContext用于创建Marshaller,Unmarshaller。Marshaller用于序列化,Unmarshaller用于反序列化。

    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);
    
    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 使用方式

    <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.4.20</version>
    </dependency>
    
  • 配置权限,设置最低权限,或者指定类(这种方式需要涉及到的所有类,如果用到了泛型,需要把实际类型也加进来)
  • XStream xStream = new XStream();
    // 允许任何类型反序列化
    xStream.addPermission(AnyTypePermission.ANY);
    // 允许自定义类型反序列化(需要所有类)
    xStream.allowTypes(new Class[]{UserDO.class, FavoriteDO.class});
    
  • 序列化,使用编程方式
  • 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);
    
  • 反序列化,使用编程方式
  • 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在配置阶段是非线程安全,配置完成后执行时是线程安全的
  • 从maven仓库上看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 使用方式

    <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
      <version>2.15.0</version>
    </dependency>
    
  • 序列化和反序列化
    通过使用XmlMapper进行序列化和反序列化
  • 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配置好之后,执行时是线程安全的
  • 四、框架比较

    XStream Jackson 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