『Java安全』XStream基础使用与序列化和反序列化源码浅析

简介

XStream is a simple library to serialize objects to XML and back again.

依赖

XStream使用了XML解析器xpp3_min

        <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.6</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/xpp3/xpp3_min -->
        <dependency>
            <groupId>xpp3</groupId>
            <artifactId>xpp3_min</artifactId>
            <version>1.1.4c</version>
        </dependency>

基础使用demo

以下分析均采用此demo

JavaBean对象

写两个JavaBean

package demo;

public class Score {
    private String subject;
    private int point;

    public Score(String subject, int point) {
        this.subject = subject;
        this.point = point;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public int getPoint() {
        return point;
    }

    public void setPoint(int point) {
        this.point = point;
    }

}

package demo;

import java.util.ArrayList;
import java.util.List;

public class Student {
    private String name;
    private String id;
    private List<Score> scores = new ArrayList<Score>();

    public Student(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Score> getScores() {
        return scores;
    }

    public void setScores(List<Score> scores) {
        this.scores = scores;
    }

    public void addScore(Score score){
        scores.add(score);
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

代码

package demo;

import com.thoughtworks.xstream.XStream;

public class Demo {
    public static void main(String[] args) {
        Score math = new Score("math", 99);
        Student student = new Student("Bob", "114514");
        student.addScore(math);

        XStream xStream = new XStream();

		// 序列化
        String string = xStream.toXML(student);
        System.out.println(string);

		// 反序列化
        Student student1 = (Student) xStream.fromXML(string);
        System.out.println(student1.toString()+ " " +student1.getName() + " " +student1.getScores().get(0).getSubject());
    }
}

序列化结果

<demo.Student>
  <name>Bob</name>
  <id>114514</id>
  <scores>
    <demo.Score>
      <subject>math</subject>
      <point>99</point>
    </demo.Score>
  </scores>
</demo.Student>

反序列化结果

demo.Student@5f9d02cb Bob math

同一对象序列化和反序列化的一致性

对于相同XStream,反复序列化和反序列化同一个对象,结果都是一致的

        XStream xStream = new XStream();
        String string = xStream.toXML(student);
        System.out.println(string);
        
        Student student1 = (Student) xStream.fromXML(string);
        String string1 = xStream.toXML(student1);
        
        System.out.println(string.equals(string1));

在这里插入图片描述

进阶操作

给标签起别名

		XStream xStream = new XStream();
        xStream.alias("StuInfo", Student.class);
        String string = xStream.toXML(student);

在这里插入图片描述

将对象属性转化为XML标签属性

        XStream xStream = new XStream();
        xStream.useAttributeFor(Student.class, "name");
        xStream.useAttributeFor(Student.class, "id");
        String string = xStream.toXML(student);

在这里插入图片描述

添加隐式集合

对于集合的序列化和反序列化,告知XStream核心能够简化操作

        XStream xStream = new XStream();
        xStream.addImplicitCollection(Student.class, "scores");xStream.useAttributeFor(Student.class, "name");
        String string = xStream.toXML(student);

实际体现就是:给demo对象再添加一个score

在这里插入图片描述
此时List<Score>的标签就被自动忽略了,正常应该是<scores> <demo.Score></demo.Score>... </scores>

在这里插入图片描述
同样的反序列化时候直接多条<demo.Score>合并写入即可,XStream核心已经知晓此处是一个隐式的集合

如果没有事先声明此处是隐式集合,只把多条集合元素合并写入会抛错

设置变量不被序列化

        XStream xStream = new XStream();
        xStream.omitField(Student.class, "scores");
        String string = xStream.toXML(student);

在这里插入图片描述

XStream核心结构图

原链接见参考

浅析XStream序列化操作源码

调用toXML()时,首先new了一个StringWriter作为输出

在这里插入图片描述
之后这个StringWriter转换成了一个PrettyPrintWriter用于优化分层结构的XML输出,然后进入marshal方法

在这里插入图片描述
传入convertLookup和mapper

在这里插入图片描述
原链接见参考
接下来就是新建树编组器,然后开始编组

在这里插入图片描述
原链接见参考
首先进行头节点也就是整个类最外层的XML编组,然后convertAnother方法开始转换对象

在这里插入图片描述
来到doMarshal(),首先调用反射获取属性

在这里插入图片描述
然后放到List中

在这里插入图片描述
如果涉及可迭代对象,对内部有处理,流程也是调用marshal()到convertAnother()到visitSerializableFields()

在这里插入图片描述
在这里插入图片描述

层次化体现在使用栈和路径的概念来处理包含关系

在这里插入图片描述

回到doMarshal(),当所有属性都添加到fields中,就开始写入writer了,一个迭代器循环

在这里插入图片描述
调用writeField序列化属性,这里传入了五个参数:属性名称、别名、类型、所属类、值

在这里插入图片描述
首先写入左边的XML标签

在这里插入图片描述
然后调用marshallField写入值,最后再写入右XML标签

在这里插入图片描述
写入值时单独提取了出来,也就是说无论是键还是值,序列化操作都是提取成item转换,只是在标签左右尖括号等处有特殊额外写入处理

在这里插入图片描述
写入值最终调用了PrettyPrintWriter.setValue

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

浅析XStream反序列化操作源码

反序列化的流程大概与序列化一致

调用MarshallingStrategy.unmarshal解组
在这里插入图片描述
首先调用HierarchicalStreams.readClassType获取反序列化的类,然后调用convertAnother进行复原

在这里插入图片描述
获取converter转换器,进去看看细节

在这里插入图片描述
对于普通Bean,默认是ReflectionConverter,从缓存中获取的;如果缓存没有下面就会查找合适的converter

在这里插入图片描述
有很多个converter
在这里插入图片描述
值得注意的是,有两个特殊converter:一般Bean是反射converter,而实现了serializable接口的类走的是serializableConverter

在这里插入图片描述
serializableConverter的介绍:看介绍是可以调用readObject方法模拟正常反序列化流程

在这里插入图片描述
往后来到AbstractReflectionConverter.unmarshal,调用instantiateNewInstance新建反序列化类的实例,然后调用doUnmarshal给它添加所有的属性参数

在这里插入图片描述

参考

https://x-stream.github.io/index.html
https://github.com/jakingting/Xtream-jar/tree/master/XStreamUML
https://blog.csdn.net/u014565127/article/details/104419528
https://www.jianshu.com/p/387c568faf62

欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://ho1aas.blog.csdn.net/article/details/126250860
版权声明:本文为原创,转载时须注明出处及本声明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值