Gson反序列化原理

news2025/1/11 23:34:27

前言

序列化和反序列化是日常工作中经常使用的工具,一般用于对象的持久化保存或者对象的网络传输,一般有两种情况,一种是对象本身实现了Serializable接口,这种情况下可以利用jdk自带的功能或者Kryo等这种封装好的序列化反序列化工具,还有一种情况就是对象本身并没有实现Serializable接口,那么这个时候开发者们就会考虑将对象序列化为字符串来进行持久化保存或传输。

笔者在日常开发中常用到的序列化工具就是谷歌的Gson和阿里巴巴的FastJson,这两种的区别在上一篇博客中通过实验的方式做了一个简单对比。那么本篇来着重探究一下Gson是如何反序列化对象的。

Gson反序列化对象实现的实现原理

我们先来考虑一个问题,如果有这样一个json字符串{“name”:“cz”,“age”:18},在没有现成的反序列化工具时,我们要如何将该json字符串中的内容映射到对象中呢?
对象的结构如下:

public class Student {
    public String name;  // 特别注意这里修饰符为public
    public String home;
    public Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getHome() {
        return home;
    }
    public void setHome(String home) {
        this.home = home;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}
方法一:

通过反射调用set方法来设置值。
代码如下:

/**
* @author Chengzhi
* @date 2023-08-23
* @测试目的:测试通过set方法来反序列化对象
* @预期结果:
*/
@Test
public void test2() throws IllegalAccessException, InstantiationException, InvocationTargetException {
   String json = "{"name":"cz","age":18}";
   String[] fields = json.substring(1,json.length()-1).split(",");
   Map<String, Object> map = new HashMap<String, Object>();
   for (String field : fields) {
       String[] split = field.split(":");
       String key = split[0];
       String value = split[1];
       if (key.startsWith(""") && key.endsWith(""")) {
           key = split[0].substring(1,split[0].length()-1);
       }
       if (value.startsWith(""") && value.endsWith(""")) {
           value = split[1].substring(1,split[1].length()-1);
       }
       map.put(key.toUpperCase(), value);
   }
   Class<Student> studentClass = Student.class;
   Student o = studentClass.newInstance();
   Method[] methods = studentClass.getMethods();
   for (Method method : methods) {
       String methodName = method.getName();
       if (methodName.startsWith("set")) {
           String key = methodName.substring(3, methodName.length()).toUpperCase();
           Class<?> parameterType = method.getParameterTypes()[0];
           Object value = map.get(key);
           if ("java.lang.Integer".equals(parameterType.getName())) {
               value = Integer.valueOf((String) value);
           }
           method.invoke(o, value);
       }
   }

   System.out.println(o.getName());
}
方法二:

在本案例中由于对象的变量是使用public修饰的,那么同样的我可以通过反射直接赋值。
代码如下:

/**
 * @author Chengzhi
 * @date 2023-08-24
 * @测试目的: 通过反射直接给对象的变量赋值
 * @预期结果:
 */
@Test
public void test3() throws IllegalAccessException, InstantiationException {
    String json = "{"name":"cz","age":18}";
    String[] jsonFields = json.substring(1,json.length()-1).split(",");
    Map<String, Object> map = new HashMap<String, Object>();
    for (String field : jsonFields) {
        String[] split = field.split(":");
        String key = split[0];
        String value = split[1];
        if (key.startsWith(""") && key.endsWith(""")) {
            key = split[0].substring(1,split[0].length()-1);
        }
        if (value.startsWith(""") && value.endsWith(""")) {
            value = split[1].substring(1,split[1].length()-1);
        }
        map.put(key.toUpperCase(), value);
    }
    Class<Student> studentClass = Student.class;
    Student o = studentClass.newInstance();
    Field[] fields = studentClass.getFields();
    for (Field field : fields) {
        String name = field.getName();
        Object value = map.get(name.toUpperCase());
        if ("java.lang.Integer".equals(field.getGenericType().getTypeName())) {
            value = Integer.valueOf((String) value);
        }
        field.set(o, value);
    }

    System.out.println(o.getName());
}
上述方式存在的问题

上述方式一其实就是阿里巴巴的FastJson实现原理,而第二种实现方式由于示例POJO类中的变量都是使用public修饰的,可以直接反射赋值,但是,正常的对象都是不允许使用public修饰,因为这样值很容易被改变。如果pojo类中的变量使用private修饰,由于访问权限的约束,Field[] fields = studentClass.getFields();这里会获取不到参数值。最终不会赋值成功。

Gson反序列化原理

其实Gson反序列化原理和方式二思路很接近,都是直接去操作POJO类的全局变量,而不是去调用set方法,那么Gson是如何绕过变量修饰符的权限约束呢。从源码可以发现Gson是利用Unsafe类的putObject方法。

//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);

该方法可以忽略修饰符的权限约束。

image.png

总结

Gson利用反射的思想结合强大的Unsafe类,通过直接改变类的变量值来达到反序列化的目的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1099901.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10.Linear Map transformation rules

线性映射 从一个基底到另一个基底 所遵循的转换规则。 假设&#xff1a; 由一个矩阵给出的线性映射在这&#xff0c;并且是在基底e上表示&#xff0c; 该线性映射将e1变成0.5个e1&#xff0c;将e2变成2个e2&#xff1b; 假设有个向量V&#xff0c;其分量是【1&#xff0c;1…

福昕阅读器打开pdf文档时显示的标题不是文件名

0 Preface/Foreword 1 现象 文件名为&#xff1a;Demo-20231017 打开效果&#xff1a;显示名字为 word template 2 解决方法 2.1 利用打印方式将word生产pdf 在word生产pdf文件时&#xff0c;使用打印方式生成pdf文档。 2.2 删除word文档设置的标题 文件---》信息---》标…

“赔率”和“概率”

赔率与概率不同 Odds ! Probability 许多人将“赔率”和“概率”这两个词混为一谈。它们都表示对可能性或机会的估计。我可以理解普通人这样做&#xff0c;但我经常看到数据科学家和统计学家也混淆了这些概念&#xff0c;这是一种遗憾&#xff0c;因为在数学上它们有不同的含…

LVGL_基础控件btnmatrix

LVGL_基础控件btnmatrix 1、创建按钮矩阵 /* 分配按钮以及按钮上的文字 */ // 这里注意不要使用在函数退出时会被销毁的局部变量 // 可以用 "\n" 进行换行&#xff0c;最后一个按钮之后的元素必须是 NULL 或 "" // 换行 "\n" 和 "NULL&quo…

Z-Fighting问题解决(二) - Reverse-z

Z值相关技术概论理清 开始之前先简单理清科普一下涉及的跟三维图形学相关的深度Z相关的概念&#xff1a; Z-Buffer&#xff1a;上个世纪八十年代的图形学飞跃节点算法之一&#xff1b; Z缓冲是一种解决深度排序问题的方法&#xff0c;主要用于确定哪些物体在其他物体的前面。…

【Oracle】分析函数partition by,解决了使用group by后select语句中只能是分组的字段或者是一个聚合函数的问题

首先我们看一下group by的用法&#xff0c;比如根据省份分组。 select province, sum(persons) from t_person group by province;使用了group by后&#xff0c;select语句中只能是分组的字段&#xff08;比如上面的province&#xff09;或者是一个聚合函数&#xff08;比如co…

【特纳斯电子】基于单片机的火灾监测报警系统-仿真设计

视频及资料链接&#xff1a;基于单片机的火灾监测报警系统-仿真设计 - 电子校园网 (mcude.com) 编号&#xff1a; T0152203M-FZ 设计简介&#xff1a; 本设计是基于单片机的火灾监测报警系统&#xff0c;主要实现以下功能&#xff1a; 1.通过OLED显示温度、烟雾、是否有火…

jenkins pipeline使用

1、jenkins全局配置 1.1、maven配置 1.2、jdk配置 1.3、git配置 2、构建环境配置 2.1、安装时间插件 Date Parameter 2.2、Git Parameter 插件安装 3、pipeline如下 pipeline {agent anyenvironment {image_name "192.168.122.150/ken-test/price-service:${date}&…

jmeter监控服务器的资源使用

一. 下载安装包SeverAgent-x.x.x.zip并安装到被监控服务器 下载地址&#xff1a;https://github.com/undera/perfmon-agent 下载完解压后执行运行&#xff0c;windows运行startAgent.bat, linux运行startAgent.sh 二. 在jmeter上添加插件jpgc-PerfMon Metrics Collector监听器…

电商API接口的发展,电商API接口主要应用场景:

随着互联网技术的不断进步和电商行业的迅猛发展&#xff0c;电商API接口在商品交易、物流配送、客户服务等方面发挥着越来越重要的作用。本文将深入探讨电商API接口的技术原理、应用场景、开发方法以及优缺点。 一、技术原理 电商API接口是基于HTTP、TCP、IP等网络协议实现的…

代码随想录二刷 Day41

509. 斐波那契数 这个题简单入门&#xff0c;注意下N小于等于1的情况就可以 class Solution { public:int fib(int n) {if (n < 1) return n; //这句不写的话test能过但是另外的过不了vector<int> result(n 1); //定义存放dp结果的数组&#xff0c;还要定义大小r…

基于复合优化加速算法研究实际问题

import optimtool as oo from optimtool.base import np, sp, pltpip install optimtool>2.5.0加载hybird.nesterov.accer方法 import optimtool.hybrid as oh nes_acc oh.nesterov.accer初始化输入数据 f ( x ) ∑ i 1 n ( ( n − ∑ j 1 n cos ⁡ x j ) i ( 1 − co…

jenkins出错与恢复

如果你的jenkins出现了如下图所示问题&#xff08;比如不能下载插件&#xff0c;无法保存任务等&#xff09;&#xff0c;这个时候就需要重新安装了。 一、卸载干净jenknis 要彻底卸载 Jenkins&#xff0c;您可以按照以下步骤进行操作&#xff1a; 1、停止 Jenkins 服务&…

学信息系统项目管理师第4版系列29_信息系统治理

1. IT治理 1.1. 描述组织采用有效的机制对信息技术和数据资源开发利用&#xff0c;平衡信息化发展和数字化转型过程中的风险&#xff0c;确保实现组织的战略目标的过程 1.2. 驱动因素 1.2.1. 信息孤岛 1.2.2. 信息资源整合目标空泛 1.3. 高质量IT治理因素 1.3.1. 良好的I…

flask基础开发知识学习

之前做了一些LLM的demo&#xff0c;接口用flask写的&#xff0c;但是涉及到后端的一些业务就感觉逻辑写的很乱&#xff0c;代码变成屎山&#xff0c;于是借助官方文档和GPT迅速补了一些知识&#xff0c;总结一下一个很小的模板 于是决定边学边重构之前的代码… 文章目录 代码结…

吃鸡达人必备神器,提升战斗力享受顶级游戏干货!

大家好&#xff01;今天我为大家介绍一款专为吃鸡玩家打造的神器——吃鸡盒子。无论您是新手还是老玩家&#xff0c;吃鸡盒子都能帮助您提升游戏的战斗力&#xff0c;分享顶级游戏作战干货&#xff0c;并且还能方便吃鸡作图、查询库存和保护账号安全。 让我们先来说说提升战斗力…

使用Premiere、PhotoShop和Audition做视频特效

今天接到一个做视频的任务&#xff0c;给一个精忠报国的视频&#xff0c;要求&#xff1a;   ①去掉人声&#xff0c;就是将唱歌的人声去掉&#xff0c;只留下伴奏&#xff1b;   ②截图视频中的横幅&#xff0c;做一个展开的效果&#xff0c;类似卷纸慢慢展开&#xff1b;…

Golang学习:基础知识篇(二)—— 数组及切片

Golang学习&#xff1a;基础知识篇&#xff08;二&#xff09;—— 数组及切片 前言什么是Golang&#xff1f;Go语言的基础语法数组声明数组初始化数组访问数组知识点补充 切片定义切片切片初始化len() 和 cap() 函数空(nil)切片切片截取append() 和 copy() 函数知识点补充 前言…

【版本控制】Git(学习笔记)

一、Git工作流程图 clone&#xff08;克隆&#xff09;: 从远程仓库中克隆代码到本地仓库checkout &#xff08;检出&#xff09;&#xff1a;从本地仓库中检出一个仓库分支然后进行修订add&#xff08;添加&#xff09;: 在提交前先将代码提交到暂存区commit&#xff08;提交&…

Python--逻辑运算符(与或非) and or not

逻辑运算符&#xff08;与或非&#xff09; not就是取反&#xff0c;只有一个表达式not 表达式&#xff0c;如果表达式为True&#xff0c;则not以后就返回False。反之&#xff0c;则返回True。 案例&#xff1a;讲个非诚勿扰的小故事&#xff1a; ① 女孩子要求比较高&#…