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

发布于 2022-09-11 20:26:30 字数 2529 浏览 19 评论 0

我有一个类如下:

@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"])] 

这种如何解决?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我一向站在原地 2022-09-18 20:26:30

实体类实现Serializable

冷心人i 2022-09-18 20:26:30

这个问题的根本原因是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());
            }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文