问题描述:
项目上使用Jackson2JsonRedisSerializer 给value做序列化。近期由于在进行项目重构,调整了包的组织结构,导致存在载redis对象class路径发生变化。在发布后,读取redis里面数据解析失败。
原因分析:
@Data
public class Person {
/**
* 年龄
*/
private int age;
/**
* 姓名
*/
private String name;
/**
* 是否成年
*/
private boolean adult;
/**
* 性别,1:男,2:女,0:未知
*/
private int sex;
/**
* 身高,单位:cm
*/
private int height;
public static void main(String[] args) {
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(om);
Person baseBO = new Person();
byte[] data = serializer.serialize(baseBO);
String string = new String(data);
System.out.println(string);
}
}
["com.johar.test.easyruletest.test.Person",{"age":0,"name":null,"adult":false,"sex":0,"height":0}]
从这个demo可以看出在Jackson2JsonRedisSerializer序列化的时候,将class的全路径类型放在json字符串中,反序列化时再根据这个全路径类型进行反序列化,因此在路径发生变化时,会反序列化失败。
下面源码中,序列化的部分,从函数的名字可以看出序列化的时候,将类型带到字符串中。
public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException
{
Object id = typeIdDef.id;
final JsonToken valueShape = typeIdDef.valueShape;
if (canWriteTypeId()) {
typeIdDef.wrapperWritten = false;
// just rely on native type output method (sub-classes likely to override)
writeTypeId(id);
} else {
// No native type id; write wrappers
// Normally we only support String type ids (non-String reserved for native type ids)
String idStr = (id instanceof String) ? (String) id : String.valueOf(id);
typeIdDef.wrapperWritten = true;
Inclusion incl = typeIdDef.include;
// first: can not output "as property" if value not Object; if so, must do "as array"
if ((valueShape != JsonToken.START_OBJECT)
&& incl.requiresObjectContext()) {
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
}
switch (incl) {
case PARENT_PROPERTY:
// nothing to do here, as it has to be written in suffix...
break;
case PAYLOAD_PROPERTY:
// only output as native type id; otherwise caller must handle using some
// other mechanism, so...
break;
case METADATA_PROPERTY:
// must have Object context by now, so simply write as field name
// Note, too, that it's bit tricky, since we must print START_OBJECT that is part
// of value first -- and then NOT output it later on: hence return "early"
writeStartObject(typeIdDef.forValue);
writeStringField(typeIdDef.asProperty, idStr);
return typeIdDef;
case WRAPPER_OBJECT:
// NOTE: this is wrapper, not directly related to value to output, so don't pass
writeStartObject();
writeFieldName(idStr);
break;
case WRAPPER_ARRAY:
default: // should never occur but translate as "as-array"
writeStartArray(); // wrapper, not actual array object to write
writeString(idStr);
}
}
// and finally possible start marker for value itself:
if (valueShape == JsonToken.START_OBJECT) {
writeStartObject(typeIdDef.forValue);
} else if (valueShape == JsonToken.START_ARRAY) {
// should we now set the current object?
writeStartArray();
}
return typeIdDef;
}
解决方案:
简单的方法就是,先将对象序列化成字符串,再添加redis,可以解决此问题