asarray java_使用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 getBean(String beanName) {

assertApplicationContext();

return (T) applicationContext.getBean(beanName);

}

public static T getBean(Class 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 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控制台看一下 :

发现写入进去的是这样的

7b44de7efc61b3db9a3969fc908864aa.png

看似没错,但是为什么使用java读取就报错了呢 ?

第三部分:排查问题

这时候 排查问题的思路是:我们来使用java写入,并且使用java读取, 总不会出问题了吧?

我们修改上述主类的方法如下:

public static void getStudents() {

System.out.println("*************getStudentHash*************");

RedisTemplate 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控制台 看下存进去的是什么格式

发现写进去的是这样的:

e4a16d7c1ca39c9e3ff805f6a34b7b4d.png

原来这和python写进去的格式不一样啊,那么肯定是各个语言自己的序列化机制的问题了

那么大胆猜想一下啊, 如果我使用python写入的时候,拼凑成这种带包名+类名的格式, 是不是就可以正常读取了呢?

来仔细对比一下两者的差别吧:

5ec0277feae0d9d222ed0eb079dd133d.png

后者居然是一个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))

打开控制台检验一下 ,看看写进去的是什么样的

4274bbc223ac9aa2f2d76608b4874624.png

结构和java写进去的一模一样, 完美

那接着来用java程序读一下 :

public static void getStudents() {

System.out.println("*************getStudentHash*************");

RedisTemplate 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 redisTemplate(RedisConnectionFactory factory) {

RedisTemplate template = new RedisTemplate();

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;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值