JSON解析问题:Type id handling not implemented for

我有一个类如下:

@CompileStatic
@JsonSerialize(using = JsonSerializer)
@JsonDeserialize(using = JsonDeserializer)
class SensitiveString {
    private String rawString

    SensitiveString(String raw) {
        rawString = raw
    }

    Object asType(Class clazz) {
        assert clazz == String: "Cannot cast Sensitive String to type other than string. "
        return this.toString()
    }

    @Override
    String toString() {
        return rawString
    }

    boolean equals(SensitiveString other){
        return other.rawString == rawString
    }

    static class JsonSerializer extends StdSerializer<SensitiveString> {

        JsonSerializer() {
            super(SensitiveString.class)
        }

        @Override
        void serialize(SensitiveString value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            if (value == null || value.rawString == null) {
                gen.writeNull()
                return
            }
            gen.writeString(SensitiveDataGuard.INSTANCE.encryptData(value.rawString))
        }
    }

    static class JsonDeserializer extends StdDeserializer<SensitiveString> {

        JsonDeserializer() {
            super(SensitiveString.class)
        }

        @Override
        SensitiveString deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            String encrypted = p.readValueAs(String)
            if (encrypted == null)
                return null
            return new SensitiveString(SensitiveDataGuard.INSTANCE.decryptData(encrypted))
        }
    }
}

然后我有一个类,里面用到这个SensitiveString

class User{
    String name;
    SensitiveString passed;
    ...
}

然后我用redis做mybatis二级缓存,json解析用的GenericJackson2JsonRedisSerializer

        redisTemplate.setKeySerializer(new StringRedisSerializer())
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer())
        redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer())
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer())

当mapper里有listUsers()方法时,mybatis需要把一个List<User>序列化加到二级缓存中去,序列化的时候出错,错误信息是

 [Type id handling not implemented for type mypackage.SensitiveString (by serializer of type mypackage.SensitiveString$JsonSerializer) (through reference chain: java.util.ArrayList[0]->mypackage.User["passwd"])] 

这种如何解决?

阅读 10.8k
3 个回答

实体类实现Serializable

这个问题的根本原因是java的范型擦除导致的。java官方为了弥补这一缺陷设计了 java.lang.reflect.Type
来表示类型信息。

一些json序列化框架 比如 jackson gson fastjson等 都有针对 type信息等处理方案。
jackson 如下

 public <T> T readValue(String content, JavaType valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readMapAndClose(_jsonFactory.createParser(content), valueType);
    } 

springcache当把缓存序列化换成json并且 序列化对象含有范型信息时便会有此问题。

你上面提到的 GenericJackson2JsonRedisSerializer 便是解决这个问题的一个方案 但是它相当不友好。因为它的实现是把类型信息写到生成到json串里面去了。

其实针对缓存这种基于aop做到东西完全是可以优雅到解决这个类型问题的。下面是我自己实现的缓存解决这个问题的一个代码段。

你可以参考 然后修改下 springcache的源码

 String key = namespace + RedisCacheKeyUtil.parseKey(redisCacheable.key(), methodInvocation.getArguments());
            Type genericReturnType = method.getGenericReturnType();
            JavaType javaType = JsonMapper.jsonMapper.getTypeFactory().constructType(genericReturnType);
            String json = redisTemplate.opsForValue().get(key);
            if (StringUtils.isNotBlank(json)) {
                Object ret = null;
                try {
                    ret = jsonMapper.readValue(json, javaType);
                    log.debug("从 redis缓存获取数据 key:{}", key);
                } catch (Exception e) {
                    log.error("redis cache error!!!", e);
                    log.error(json, e);

                    proceedAndSave(methodInvocation, key, redisCacheable.expireTime());

                }

                return ret;
            } else {
                return proceedAndSave(methodInvocation, key, redisCacheable.expireTime());
            }
新手上路,请多包涵
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题