文档章节

使用Java读取 “Python写入redis” 的数据踩坑记录

沧海一刀
 沧海一刀
发布于 06/13 11:34
字数 1295
阅读 606
收藏 4
  • 前置环境声明:

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;
	}

}

 

© 著作权归作者所有

沧海一刀
粉丝 8
博文 76
码字总数 46921
作品 0
黄浦
程序员
私信 提问
加载中

评论(7)

听风的小猪
听风的小猪

引用来自“milin”的评论

小伙儿,这个你绕远了,通用json你java按照字符串读取,然后再使用fastjson反向解析就好了。你一修改整个都不是json格式了,也就不具备json优势了
你的说法才是对的!
JPer
JPer
json的意义是什么,楼主搞明白了再说…
milin
milin
小伙儿,这个你绕远了,通用json你java按照字符串读取,然后再使用fastjson反向解析就好了。你一修改整个都不是json格式了,也就不具备json优势了
007
007

引用来自“007”的评论

根本不是不同语言序列化及时差异,python你用了通用的json序列化(如果要说序列化差异应该用python的pickle和java的序列化比较),java却不用接送去解析,最后还让python适应java特有的序列化格式,单独自己的项目没问题,但通用性没发保证(现在redis里的数据如果要在python或其他语言中读取就只能以字符串解析方式读了),正确的方式是redis里存储json字符串,任何语言都有解析和生成json的功能,java直接解析json数据写入类中就可以了
有部分我看错了,不过在python里把java的包名写进去不太好,除了java之外的语言都没有意义
007
007
根本不是不同语言序列化及时差异,python你用了通用的json序列化(如果要说序列化差异应该用python的pickle和java的序列化比较),java却不用接送去解析,最后还让python适应java特有的序列化格式,单独自己的项目没问题,但通用性没发保证(现在redis里的数据如果要在python或其他语言中读取就只能以字符串解析方式读了),正确的方式是redis里存储json字符串,任何语言都有解析和生成json的功能,java直接解析json数据写入类中就可以了
HingBox
HingBox
不错
逐梦的人
牛🐮
在Java工程中调用Python代码

Python是一门好用的脚本语言,底层基于C实现,快速且简单。使用python可以说是站在巨人肩膀上编程,使得程序员更专注于业务及逻辑,而不是被人学烂的语言工具。但是团队合作时候,就会有一些...

辉煌霸猪
2018/09/12
0
0
如何让 J2Cache 在多种编程语言环境中使用

现在的系统是越来越复杂了,不仅仅是功能复杂,系统结构也非常复杂,而且经常在一个系统里包含几种不同语言编写的子系统。例如用 JavaScript 做前端开发、用 Java/PHP 等等做后端,C/C++/Go ...

红薯
2018/10/17
2.6K
9
『OGG 02』Win7 配置 Oracle GoldenGate Adapter Java 踩坑指南

上一文章 《Win7 配置OGG(Oracle GoldenGate).docx》定下了 两个目标: 目标1: 给安装的Oracle_11g 创建 两个用户 admin 和 root 。 admin 对应了 ADMIN 结构,创建了一个 TB_ TEST表。 ro...

InkFx
2018/06/30
0
0
【Spark】Spark SQL, DataFrames and Datasets Guide(翻译文,持续更新)

本文主要是翻译Spark官网Spark SQL programming guide 。只能保证大概意思,尽量保证细节。英文水平有限,如果有错误的地方请指正,轻喷。目录导航在右上角 Spark SQL、DataFrames 和 Datase...

跑呀跑
2018/09/19
0
0
为什么 Python 不用设计模式?

作者 | 刘欣 责编 | 伍杏玲 本文经授权转自码农翻身(ID:coderising) 在遥远的Python王国,有一位少年,非常热爱编程,他的父母想给他报一个班,问了万能的朋友圈以后,发现大家都推荐同一...

CSDN资讯
03/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

局域网能互相ping通,ubuntu虚拟机不能上外网

【问题】 桥接模式老是无法上网,查看本机IP发现被分配了一个私网地址,猜测应该是虚拟DHCP服务器没有打开,于是查看Ubuntu的网络配置: /etc/network/interfaces 发现没有dhcp配置的信息,只...

tahiti_aa
55分钟前
1
0
以太坊助记词PHP开发包简介

以太坊助记词PHP开发包用来为PHP以太坊应用增加助记词和层级确定密钥支持能力。下载地址:以太坊助记词php开发包 。 1、开发包概述 以太坊助记词PHP开发包主要包括以下特性: 生成符合BIP39...

汇智网教程
昨天
2
0
系统监控-分布式调用链Skywalking

1. 为什么要使用分布式调用链技术? 随着公司业务的高速发展,公司服务之间的调用关系愈加复杂,如何理清并跟踪它们之间的调用关系就显的比较关键。线上每一个请求会经过多个业务系统,并产生...

秋日芒草
昨天
4
0
告诉自己的一些建议

摆脱学生心态 尽快发挥自己价值,让公司感知自己的存在,才是王道 选择比努力重要 自己附着的平台的经济体要是一个快速崛起的行业 转行趁早,年龄越大选择成本越高 趁早大量试错,学习新领域...

林怡丰
昨天
4
0
Windows下安装Redis

下载地址: 3.0老版已不维护更新:https://github.com/MicrosoftArchive/redis/releases 4.0版 https://github.com/tporadowski/redis/releases 中文官网:http://www.redis.net.cn/ https:......

Aeroever
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部