使用Java读取 “Python写入redis” 的数据踩坑记录
-
前置环境声明:
java8 + spring-boot-2.0.0.RELEASE + spring-boot-starter-data-redis(boot集成)
python 3.7 64位
redis版本4.0.1
在这个python脚本中写入redis的数据是3个Student对象
代码如下
-
第一部分:python写入
import redis
import json
redisOperator = redis.Redis(host='localhost', port=6379,password="666666")
class Student(object):
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
student1 = Student("chenjun1", 18, "男")
student2 = Student("chenjun2", 19, "男")
student3 = Student("chenjun3", 20, "男")
studentList = []
studentList.append(student1)
studentList.append(student2)
studentList.append(student3)
print(json.dumps(student1.__dict__, ensure_ascii=False))
print(json.dumps(student2.__dict__, ensure_ascii=False))
print(json.dumps(student3.__dict__, ensure_ascii=False))
for u in studentList:
redisOperator.hset("ThreeCodeInOne",u.name, json.dumps(u.__dict__, ensure_ascii=False))
-
第二部分 ,java读取:
@SpringBootApplication
public class Application implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Application.applicationContext = applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
assertApplicationContext();
return (T) applicationContext.getBean(beanName);
}
public static <T> T getBean(Class<T> requiredType) {
assertApplicationContext();
return applicationContext.getBean(requiredType);
}
private static void assertApplicationContext() {
if (Application.applicationContext == null) {
throw new RuntimeException("applicaitonContext属性为null,请检查是否注入了SpringContext!");
}
}
public static void getStudents() {
System.out.println("*************getStudentHash*************");
RedisTemplate<String, String> redisTemplate = getBean("redisTemplate");
redisTemplate.setValueSerializer(new GenericToStringSerializer<>(Object.class, Charset.forName("UTF-8")));
Object o = redisTemplate.opsForHash().get("ThreeCodeInOne", "chenjun1");
System.out.println(o);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
assertApplicationContext();
getStudents();
}
}
然而,这样是错误的!
然而,这样是错误的!
然而,这样是错误的!
报错信息:
*************getStudentHash*************
Exception in thread "main" 2019-06-13 11:10:03.813 INFO 7064 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@51931956: startup date [Thu Jun 13 11:10:01 CST 2019]; root of context hierarchy
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
at [Source: (byte[])"{"name": "chenjun1", "age": 18, "sex": "男"}"; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.lang.Object
at [Source: (byte[])"{"name": "chenjun1", "age": 18, "sex": "男"}"; line: 1, column: 1]
at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.deserialize(Jackson2JsonRedisSerializer.java:75)
at org.springframework.data.redis.core.AbstractOperations.deserializeHashValue(AbstractOperations.java:354)
at org.springframework.data.redis.core.DefaultHashOperations.get(DefaultHashOperations.java:54)
at com.redisexample.Application.getStudents(Application.java:139)
at com.redisexample.Application.main(Application.java:97)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.lang.Object
at [Source: (byte[])"{"name": "chenjun1", "age": 18, "sex": "男"}"; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1498)
at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1273)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:137)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:96)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:71)
at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:712)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3117)
at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.deserialize(Jackson2JsonRedisSerializer.java:73)
... 4 more
2019-06-13 11:10:03.815 INFO 7064 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
这是为什么呢?
我打开redis控制台看一下 :
发现写入进去的是这样的
看似没错,但是为什么使用java读取就报错了呢 ?
-
第三部分:排查问题
这时候 排查问题的思路是:我们来使用java写入,并且使用java读取, 总不会出问题了吧?
我们修改上述主类的方法如下:
public static void getStudents() {
System.out.println("*************getStudentHash*************");
RedisTemplate<String, String> redisTemplate = getBean("redisTemplate");
Student student = new Student("chenjun1", "18", "男");
redisTemplate.opsForHash().put("ThreeCodeInOne", student.getName(), student);
Object o = redisTemplate.opsForHash().get("ThreeCodeInOne", "chenjun1");
System.out.println(o);
}
执行结果:
*************getStudentHash*************
Student [name=chenjun1, age=18, sex=男]
2019-06-13 11:16:03.877 INFO 188 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@61d47554: startup date [Thu Jun 13 11:16:01 CST 2019]; root of context hierarchy
2019-06-13 11:16:03.880 INFO 188 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
这时候,再打开一下redis-cli控制台 看下存进去的是什么格式
发现写进去的是这样的:
原来这和python写进去的格式不一样啊,那么肯定是各个语言自己的序列化机制的问题了
那么大胆猜想一下啊, 如果我使用python写入的时候,拼凑成这种带包名+类名的格式, 是不是就可以正常读取了呢?
来仔细对比一下两者的差别吧:
后者居然是一个json数组, 那我们在python代码中试着拼凑一下,
我修改python代码如下:
import redis
import json
redisOperator = redis.Redis(host='localhost', port=6379,password="666666")
class Student(object):
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def useJavaSerial(student):
list = []
list.append("com.redisexample.domain.Student")
list.append(student.__dict__)
return json.dumps(list,ensure_ascii=False)
student1 = Student("chenjun1", 18, "男")
student2 = Student("chenjun2", 19, "男")
student3 = Student("chenjun3", 20, "男")
studentList = []
studentList.append(student1)
studentList.append(student2)
studentList.append(student3)
print(json.dumps(student1.__dict__, ensure_ascii=False))
print(json.dumps(student2.__dict__, ensure_ascii=False))
print(json.dumps(student3.__dict__, ensure_ascii=False))
for u in studentList:
redisOperator.hset("ThreeCodeInOne",u.name, useJavaSerial(u))
打开控制台检验一下 ,看看写进去的是什么样的
结构和java写进去的一模一样, 完美
那接着来用java程序读一下 :
public static void getStudents() {
System.out.println("*************getStudentHash*************");
RedisTemplate<String, String> redisTemplate = getBean("redisTemplate");
Object o = redisTemplate.opsForHash().get("ThreeCodeInOne", "chenjun1");
System.out.println(o);
}
输出:
*************getStudentHash*************
Student [name=chenjun1, age=18, sex=男]
2019-06-13 11:28:19.852 INFO 15932 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@564718df: startup date [Thu Jun 13 11:28:17 CST 2019]; root of context hierarchy
2019-06-13 11:28:19.853 INFO 15932 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
猜想成功验证,结论: 是不同语言序列化机制的差异导致的读取和写入造成格式不兼容
- 补充备注:
Spring-data-redis的redis序列化配置:
package com.redisexample.redisconf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
public class RedisConfig {
/**
* 自定义统一RedisTemplate序列化机制
* @param factory
* @return
*/
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
关注公众号
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
Angular CDK Overlay 弹出覆盖物
为什么使用Overlay? Overlay中文翻译过来意思是覆盖物,它是Material Design components for Angular中针对弹出动态内容这一场景的封装,功能强大、使用方便,尤其在开发自己的组件库时,可以让你少写许多代码,可以说只要是弹出内容的场景基本都可以使用Overlay. 我们自己的组件库中弹出场景基本都已经使用Overlay,如自定义Select、Cascader、Tree Select、Tooltip、Dialog等,总结最重要的的两点好处: 让使用者不再进行繁琐的位置计算,而简单通过参数配置就实现内容的定位,而且关于位置的各种情况都有考虑到. 组件的弹出内容都是用Overlay实现,避免了各自实现的产生的不兼容,如相互遮盖问题. 简单示例 - 连结位置源的弹出 下面通过一个示例代码来展示Overlay的使用,这种弹出场景类似于Tooltip,弹出的overlay内容是基于一个参照的位置源origin元素. 安装并且导入模块 项目中如果没有安装CDK,要先安装 npm install @angular/cdk 导入OverlayModule imp...
-
下一篇
BeeGFS开发环境搭建4-源码编译
如果我们需要动态调试和开发BeeGFS,那么就需要自己手动编译了,而且能够用最简单的方式运行起来,下面就讲解自己探索和分析的过程。 参考文档: # https://git.beegfs.io/pub # https://www.beegfs.io/wiki/BuildFromSources 初始化环境 安装依赖的软件包: $ yum install -y libuuid-devel libibverbs-devel librdmacm-devel libattr-devel redhat-rpm-config rpm-build xfsprogs-devel cppunit cppunit-devel zlib-devel openssl-devel sqlite sqlite-devel ant gcc-c++ gcc redhat-lsb-core java-devel 获取官方源码 获取指定版本的源码: $ git clone https://git.beegfs.io/pub/v6.git beegfs-v6 $ cd beegfs-v6 $ git tag -l 6.1 6....
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL数据库在高并发下的优化方案



微信收款码
支付宝收款码