Jackson2JsonRedisSerializer使用及问题

news2024/12/30 3:06:47

1、使用

public static RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        lettuceConnectionFactory.setShareNativeConnection(false);
        RedisTemplate<String, Object> rt = new RedisTemplate<>();
        // 设置连接工厂
        rt.setConnectionFactory(lettuceConnectionFactory);
        // 设置 key 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        rt.setKeySerializer(stringRedisSerializer);
        rt.setHashKeySerializer(stringRedisSerializer);
        // 创建 JSON 序列化工具
        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        /*ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jsonRedisSerializer.setObjectMapper(mapper);*/
        // 设置 value 的序列化
        rt.setValueSerializer(jsonRedisSerializer);
        rt.setHashValueSerializer(jsonRedisSerializer);
        rt.afterPropertiesSet();
        return rt;
    }

2、场景模拟

(1)不设置ObjectMapper直接发起set、get一个JSON对象操作结果

SET代码:

    @Test
    void redisSetTest() throws Exception {
        String jsonStr = "{\"packetId\":5,\"packetHash\":\"a828f89bfde8639d2caab1ae9b1d953f34b407af1597cce9343620b99f995334\",\"witnessNum\":100,\"remainWitnessNum\":97,\"blockNumber\":5,\"blockHash\":\"35354e07c9fb895c1c02851ca1e55c44fda87c21d87c3d47c8ffc20c8206e2a9\",\"blockTimestamp\":1682385969}";
        redisService.set("test1", JSON.parseObject(jsonStr));
    }

执行后,redis保存结果:

get代码:

    @Test
    void redisGetTest() throws Exception {
        Object test1 = redisService.get("test1");
        JSONObject jsonObject = JSON.parseObject((String) test1);
        System.out.println(jsonObject);
    }

执行结果:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to java.lang.String

 set进去的是一个JSONOBJECT,然后get出来的时候变成了LinkedHashMap。看源码:

Jackson2JsonRedisSerializer--》deserialize--》this.objectMapper.readValue--》
result = ctxt.readRootValue(p, valueType, this._findRootDeserializer(ctxt, valueType), (Object)null);

this._findRootDeserializer(ctxt, valueType)这代码最终返回的是:

UntypedObjectDeserializer

然后反序列化就看这个类的

deserialize(JsonParser p, DeserializationContext ctxt)

方法,里边mapObject源码里边就找到很多LinkedHashMap,也就是最终反序列化对象为此的缘由。

if (key1 == null) {
            return new LinkedHashMap(2);
        } else {
            p.nextToken();
            Object value1 = this.deserialize(p, ctxt);
            String key2 = p.nextFieldName();
            if (key2 == null) {
                LinkedHashMap<String, Object> result = new LinkedHashMap(2);
                result.put(key1, value1);
                return result;
            } else {
                p.nextToken();
                Object value2 = this.deserialize(p, ctxt);
                String key = p.nextFieldName();
                LinkedHashMap result;
                if (key == null) {
                    result = new LinkedHashMap(4);
                    result.put(key1, value1);
                    return result.put(key2, value2) != null ? this._mapObjectWithDups(p, ctxt, result, key1, value1, value2, key) : result;

 

 (2)设置ObjectMapper发起set、get一个JSON对象操作结果

将“使用”中注释掉的ObjectMapper代码放开,然后执行set:

    @Test
    void redisSetTest() throws Exception {
        String jsonStr = "{\"packetId\":5,\"packetHash\":\"a828f89bfde8639d2caab1ae9b1d953f34b407af1597cce9343620b99f995334\",\"witnessNum\":100,\"remainWitnessNum\":97,\"blockNumber\":5,\"blockHash\":\"35354e07c9fb895c1c02851ca1e55c44fda87c21d87c3d47c8ffc20c8206e2a9\",\"blockTimestamp\":1682385969}";
        redisService.set("test", JSON.parseObject(jsonStr));
    }

执行后,redis保存结果:

 

执行get方法:

    @Test
    void redisGetTest() throws Exception {
        Object test1 = redisService.get("test");
        JSONObject jsonObject = JSON.parseObject((String) test1);
        System.out.println(jsonObject);
    }

 返回结果,报错:java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to java.lang.String

这次是因为存redis的时候将对象的类型也同时放在VALUE中了,所以返回的时候就按照@class进行对象封装,故此时我们可以直接强转成指定的class即可。修改get方法如下:

    @Test
    void redisGetTest() throws Exception {
        Object test1 = redisService.get("test");
        System.out.println( (JSONObject) test1);
    }

此处获取的key:test是有@class的设置,但前边我们未设置ObjectMapper时设置的key:test1里边没有@class,此时获取test1的值时就报错:

org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Could not resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'

意思就是VALUE里边缺少@class属性,让他反序列化的时候不知道怎么处理了。因篇幅关系就不翻源码了。对比之前的代码主要就是后边设置ObjectMapper的属性,而这个@class是否设置根这个“JsonTypeInfo.As.PROPERTY”相关。假如把这个去掉,会不会就能获取了呢,答案时否定的,GET返回信息如下:

org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.lang.Object

这意思就是说返回值的格式不对,期望是个数组个数,结果返回的是单个OBJECT,为什么期望是数组呢。简单点我们把上一步的“JsonTypeInfo.As.PROPERTY”去掉之后,再调一下set方法,设置一个新值。观察VALUE变化,如下:

 发现,现在虽然没有@class了但是放一个OBJECT结果存到redis是个数组,把类型放到数组的第一个元素了。没法,继续上源码,看ObjectMapper源码:

    public ObjectMapper activateDefaultTyping(PolymorphicTypeValidator ptv) {
        return this.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
    }

    public ObjectMapper activateDefaultTyping(PolymorphicTypeValidator ptv, ObjectMapper.DefaultTyping applicability) {
        return this.activateDefaultTyping(ptv, applicability, As.WRAPPER_ARRAY);
    }

    public ObjectMapper activateDefaultTyping(PolymorphicTypeValidator ptv, ObjectMapper.DefaultTyping applicability, As includeAs) {
        if (includeAs == As.EXTERNAL_PROPERTY) {
            throw new IllegalArgumentException("Cannot use includeAs of " + includeAs);
        } else {
            TypeResolverBuilder<?> typer = this._constructDefaultTypeResolverBuilder(applicability, ptv);
            typer = typer.init(Id.CLASS, (TypeIdResolver)null);
            typer = typer.inclusion(includeAs);
            return this.setDefaultTyping(typer);
        }
    }

前边说的去掉“JsonTypeInfo.As.PROPERTY”,然后调用的ObjectMapper构造方法就对应前两个,我们可以看到As.WRAPPER_ARRAY,这从字面意思就大致知道需要是个ARRAY,实际也确实如此,后边再介绍这个具体作用。对一个上边的测试结果也就是,虽然去掉“JsonTypeInfo.As.PROPERTY”可以解决获取的VALUE值没有@class的问题,但是又需要VALUE值是个数组格式。

JsonTypeInfo.As介绍

  • PROPERTY 将对象类型包含在对象成员属性里
  • WRAPPER_OBJECT 将对象类型作为键,序列化的对象作为值
  • WRAPPER_ARRAY 第一个元素是对象类型,第二元素是序列化的对象

使用不同类别的VALUE呈现效果分别对应如下 :

  // Type name as a property, same as above
  {
    "@type" : "Employee",
     ...
  }
 
  // Wrapping class name as pseudo-property (similar to JAXB)
  {
    "com.fasterxml.beans.EmployeeImpl" : {
       ... // actual instance data without any metadata properties
    }
  }
 
  // Wrapping class name as first element of wrapper array:
  [
    "com.fasterxml.beans.EmployeeImpl",
    {
       ... // actual instance data without any metadata properties
    }
  ]

此时大概应该明白,为什么去掉“JsonTypeInfo.As.PROPERTY”仍然解析不出来没带对象类型的VALUE原因了。(看到这的人我很佩服你的坚持)。既然mapper.activateDefaultTyping不管怎么设置都需要对象类型,那么此时如果我们还是想要获取没有对象信息的值,怎么办呢(很多人可能对此已经没任何想法了,获取不到那就统一用有对象信息的方法放呗,折腾这鬼玩意。^_^,其实在跨语言用redis互通数据时,你就发现还是得折腾一下,O(∩_∩)O哈哈~,比如GO语言他负责SET数据,但是GO肯定是不知道JAVA的对象类型的,所以他也只能给你一个没对象信息的OBJECT,此时JAVA端不做点什么,那就只能骂娘了啊)

跨语言VALUE解析方法

上接提到的加入GO不知道JAVA对象信息,SET也没放对象信息,此时JAVA端该怎么GET数据。最简单的办法就是去掉ObjectMapper配置,此时所有类统一用LinkedHashMap处理,JAVA端GET到的数据是一个对象时,就用LinkedHashMap去接收,然后转换成自己的类。

另一种方法就是JAVA端该怎么配ObjectMapper还是怎么配,因为有ObjectMapper在JAVA端使用起来方便啊,类型放VALUE出来直接强转就完事了。让GO端SET时候直接放字符串,JAVA端用字符串GET即可。

 

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

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

相关文章

嵌入式软考备考_6 嵌入式程序设计

嵌入式程序设计 开发流程 要想某个功能要用硬件还是软件实现&#xff08;硬件快&#xff0c;但是耗资源&#xff09;&#xff1f;BSP&#xff1f;裸机还是OS&#xff1f; 硬件的设计与实现-》设备驱动软件的设计与实现-》os的选择&#xff0c;移植&#xff0c;api接口函数的…

openGL 环境搭建

刚入坑&#xff0c;每个包、每个项目都得重新配一遍&#xff0c;实在烦人&#xff0c;由于网上已有很多教程&#xff0c;故在此只简要介绍。 比较通用的安装方法如下&#xff1a; 优先下载&#xff0c;对应vs版本&#xff0c;32位&#xff0c;已经编译好的库。如果下载的是源代…

2023 年Java经典面试题,基础篇02(持续更新)

本篇文章主要讲的是 2023 年Java最新面试题&#xff0c;持续更新中 原文地址&#xff1a;https://github.com/Snailclimb/JavaGuide 面向对象基础 面向对象和面向过程的区别 两者的主要区别在于解决问题的方式不同&#xff1a; 面向过程把解决问题的过程拆成一个个方法&…

Git 配置多个SSH-Key

Git 全局配置 查看全局配置 git config --global --list git config --global user.name 删除全局配置 git config --global --unset user.name 1.生成ssh public key and private key open git base, 执行以下command&#xff0c;其中是keyname自己填&#xff1b;codeup…

java程序员容易被人误解?我来聊聊常见的三种情况

先说一下个人情况吧。21年来到深圳&#xff0c;现在马上工作满两年。我说程序员是一个容易被人误解的职业呢&#xff0c;可能在大部分人的印象里&#xff0c;程序员就是一个呆板、穿着格子衫、戴着厚厚的眼镜片、比较呆滞的一群人。其实这个印象可能是符合上个年代吧——程序员…

第二部分——长难句——第二章——复合句——第二节——定语从句

内容比较多&#xff0c;且比较重要 一&#xff0c;定语从句的概述 定语&#xff08;紧挨着名词&#xff09;礼让弱小&#xff0c;所以放在所修饰的名词后面。 法律是一种学科&#xff08;鼓励责任判定&#xff09;定语修饰 在翻译成中文的习惯&#xff0c;我们会把定语从句放…

倒计时|数字标准读书会—打破“信息孤岛”、拆除“数据烟囱”

业务与其他业务域间存在信息鸿沟&#xff0c;协作效率低下&#xff1b; 系统边界划分复杂混乱&#xff0c;技术标准不兼容&#xff1b; 新业务无法基于已有解决方案快速组装上线&#xff0c;迭代创新&#xff1b; …… 以上这些数字化转型痛点&#xff0c;你是否也遇到过&a…

家里网速越来越慢?路由器附近千万别放这几样东西

我们在日常生活中常常会使用到WiFi&#xff0c;当遇到网络卡顿、网速缓慢时往往感到疑惑&#xff0c;是不是自己的WiFi速度不够&#xff1f;其实除了无线路由器、终端等自身产品质量问题&#xff0c;还有许多外在因素。 在网络本身没有问题的情况下WiFi卡顿或不稳定&#xff0…

美国限制Unity和UE渲染引擎风险增加,数字孪生行业急需国产渲染引擎软件

数字孪生作为智能制造的关键技术之一&#xff0c;其应用已经从工业制造领域扩展到了城市规划、交通运输、农业等多个领域。然而&#xff0c;随着中美脱钩的持续进行&#xff0c;有关3D渲染引擎的“卡脖子”问题已经引起了行业的高度关注。 美国限制3D渲染引擎风险对数字孪生产…

linux中查看某个文件夹下文件的个数和大小

一、统计某个目录的文件和子目录的大小 1、stat指令 stat命令 主要用于显示文件或文件系统的详细信息&#xff0c;该命令的语法格式如下&#xff1a; -f  不显示文件本身的信息&#xff0c;显示文件所在文件系统的信息-L  显示符号链接-t  简洁模式&#xff0c;只显示…

强化学习p3-策略学习

Policy Network (策略网络) 我们无法知道策略函数 π \pi π所以要做函数近似&#xff0c;求一个近似的策略函数 使用策略网络 π ( a ∣ s ; θ ) \pi(a|s;\theta) π(a∣s;θ) 去近似策略函数 π ( a ∣ s ) \pi(a|s) π(a∣s) ∑ a ∈ A π ( a ∣ s ; θ ) 1 \sum_{a\in …

【Hive实战】数据仓库设计

数仓中的数据分层 文章目录 数仓中的数据分层维度模型维度建模下的主要概念维度表事实表 结合维度模型分析数据分层贴源层 &#xff08;Operation Data Store&#xff09;公共维度层 CDM&#xff08;Common Data Model&#xff09;数据细节层 DWD&#xff08;Data Warehouse De…

前端(移动端)学习笔记

PC端网页和移动端网页的区别&#xff1a; PC端屏幕大&#xff0c;网页固定版心移动端屏幕小&#xff0c;网页多数为100% 在谷歌浏览器中可以调试移动端网页的代码 屏幕尺寸&#xff1a; 屏幕尺寸指的是对角线的长度,一般用英寸来度量 分辨率&#xff1a; PC分辨率 1920*10…

31 - 买股票的最佳时机问题

文章目录 1. 买股票的最佳时机I2. 买股票的最佳时机II3. 最佳买卖股票时机4. 买股票的最佳时机III5. 买股票的最佳时机IV 1. 买股票的最佳时机I dp数组含义&#xff0c;本题两个状态&#xff1a;持有股票、不持有股票 dp[i][1] &#xff1a;表示第i天不持有股票所得最多现金dp…

最时髦的AI画画,一文包教包会

最时髦的AI画画&#xff0c;一文包教包会 大概半年前&#xff0c;AI 绘画工具 Disco Diffusion 从 Text-to-Image 开发社区和设计行业&#xff0c;火到了普通用户的视野中。即便它界面简陋&#xff0c;满屏英文和代码&#xff0c;也“劝退”不了人们。因为对那些没有任何美术功…

kali php无法执行,直接下载或显示php文件内容

问题描述&#xff1a; 开启apache或nginx&#xff0c;访问php文件要么直接显示php文件内容&#xff0c;要么直接下载&#xff0c;这都是php文件无法解析的问题&#xff0c;需要nginxphp-fpm解决 1、安装NGINX 查看是否安装&#xff0c;如下图则已安装 #apt-cache policy ngi…

Linux安装MinIO及springboot项目整合使用实战(详细)

以往的项目&#xff0c;用的比较多的OSS服务是腾讯云和阿里云的存储服务&#xff0c;不过从去年到今年&#xff0c;最近的几个项目&#xff0c;普遍要使用Minio,所以我在开发服务器和测试服务器上都装上了minio 一、首先minio的安装 MInIO的安装有很多方法、单实例的、集群分…

Django学习——安装、创建项目、数据库、用户管理案例

目录 1、 安装django 1.1django是第三方模块&#xff0c;用pip install django 安装&#xff1a; 1.2 python的安装目录 &#xff1a; 2、创建项目 2.1在终端创建的步骤 执行过程 2.2使用pycharm&#xff08;企业版&#xff09;创建 django项目 2.3对比两种方式 2.4默认…

推荐5款体积小、无广告、超实用的办公软件

大家好&#xff0c;我又来啦&#xff0c;今天给大家带来的几款软件&#xff0c;共同特点都是无广告、超实用&#xff0c;大家观看完可以自行搜索下载哦。 1.网络分析工具——Wireshark Wireshark 是一款开源的网络分析工具&#xff0c;它可以让你捕获和浏览网络上的数据包&am…

《我命由我不由天》蔡志忠——笔记二

目录 经典记录 天才不是天生的 如何认识自己、如何发掘和重用自己 自己拯救自己才是唯一出路 了解自己是人生第一个智慧 科学家证实&#xff0c;成就与选择目标的年龄成反比&#xff01; “努力无用论” 经典记录 天才不是天生的 美国物理学家理查德费曼两三岁时&#x…