【问题标题】:Deserialize bson long primitive json in jackson在杰克逊中反序列化bson long原始json
【发布时间】:2018-06-07 16:22:15
【问题描述】:

我使用 MongoDB 作为我们的数据存储,但我们想使用 Jackson 进行序列化/反序列化(Mongo pojo 类处理的场景几乎没有 Jackson - 例如构建器)。

我们使用自定义 CodecProvider 进行这项工作 - 这是编解码器本身:

class JacksonCodec<T> implements Codec<T> {


   private final ObjectMapper objectMapper;
    private final Codec<RawBsonDocument> rawBsonDocumentCodec;
    private final Class<T> type;

    public JacksonCodec(ObjectMapper objectMapper,
                        CodecRegistry codecRegistry,
                        Class<T> type) {
        this.objectMapper = objectMapper;
        this.rawBsonDocumentCodec = codecRegistry.get(RawBsonDocument.class);
        this.type = type;
    }

    @Override
    public T decode(BsonReader reader, DecoderContext decoderContext) {
        try {

            RawBsonDocument document = rawBsonDocumentCodec.decode(reader, decoderContext);
            String json = document.toJson();
            return objectMapper.readValue(json, type);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public void encode(BsonWriter writer, Object value, EncoderContext encoderContext) {
        try {

            String json = objectMapper.writeValueAsString(value);

            rawBsonDocumentCodec.encode(writer, RawBsonDocument.parse(json), encoderContext);

        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public Class<T> getEncoderClass() {
        return this.type;
    }
}

这工作正常,直到我们从 Mongo 检索一个 long 大于 Integer.MAXVALUE 的文档。发生这种情况时,反序列化失败并显示以下消息:

原因:com.fasterxml.jackson.databind.JsonMappingException:无法反序列化 long out of START_OBJECT 令牌的实例。

看看 bson,Mongo 数据是如何返回给我们的:

“dateStamp”:{“$numberLong”:“1514334498165”}

所以...我想我需要为 Jackson 注册一个额外的反序列化器来处理这种情况(检查 ID_START_OBJECT 的令牌类型,解析它是否存在,否则委托给内置的反序列化器)。我尝试使用 ObjectMapper SimpleModule 注册一个简单的 Long 反序列化器:

public class BsonLongDeserializer  extends JsonDeserializer<Long>{

    @Override
    public Class<Long> handledType() {
        return Long.class;
    }

    @Override
    public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        if (p.currentTokenId() != JsonTokenId.ID_START_OBJECT){
            // have to figure out how to do this for real if we can get the deserilizer to actually get called
            return ctxt.readValue(p, Long.class);
        }
        return null;
    }
}

并注册:

private static ObjectMapper createMapper(){
    SimpleModule module = new SimpleModule();
    module.addDeserializer(Long.class, new BsonLongDeserializer());

    ObjectMapper mapper = new ObjectMapper()
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    .registerModule(module);

    return mapper;
}

但是 Jackson 永远不会调用 BsonLongDeserializer(原语的处理方式是否不同并且可能会使注册的反序列化器短路?)。

杰克逊版本 2.9.3。 MongoDB 驱动程序版本 3.6。

如果有人对攻击此问题的角度有任何建议,我将不胜感激。

似乎没有帮助的参考文章:MongoDB "NumberLong/$numberLong" issue while converting back to Java Object

【问题讨论】:

    标签: java mongodb jackson jackson2


    【解决方案1】:

    我通过创建 JsonWriterSettings 对象来抑制奇怪的 json 反序列化来修复 Mongo 方面的问题。这来自这里:converting Document objects in MongoDB 3 to POJOS

    编解码器现在看起来像这样:

    class JacksonCodec<T> implements Codec<T> {
        private final ObjectMapper objectMapper;
        private final Codec<BsonDocument> rawBsonDocumentCodec;
        private final Class<T> type;
    
        public JacksonCodec(ObjectMapper objectMapper,
                            CodecRegistry codecRegistry,
                            Class<T> type) {
            this.objectMapper = objectMapper;
            this.rawBsonDocumentCodec = codecRegistry.get(BsonDocument.class);
            this.type = type;
        }
    
        @Override
        public T decode(BsonReader reader, DecoderContext decoderContext) {
            try {
                //https://stackoverflow.com/questions/35209839/converting-document-objects-in-mongodb-3-to-pojos
                JsonWriterSettings settings = JsonWriterSettings.builder().int64Converter((value, writer) -> writer.writeNumber(value.toString())).build();
    
                BsonDocument document = rawBsonDocumentCodec.decode(reader, decoderContext);
                String json = document.toJson(settings);
                return objectMapper.readValue(json, type);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }
    
        @Override
        public void encode(BsonWriter writer, Object value, EncoderContext encoderContext) {
            try {
    
                String json = objectMapper.writeValueAsString(value);
    
                rawBsonDocumentCodec.encode(writer, RawBsonDocument.parse(json), encoderContext);
    
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }
    
        @Override
        public Class<T> getEncoderClass() {
            return this.type;
        }
    }
    

    【讨论】:

    • 只是好奇,你好像在用Jackson的ObjectMapper,难道不是ObjectMapper objectMapper = new ObjectMapper(new BsonFactory()) 能做到吗?
    • 已经好几年了,所以我在这里的记忆很模糊。当时的问题(现在很可能已经解决了)是 BsonFactory 确实不能很好地使用 mongo 编解码器概念。在这一点上,我真的不记得为什么了,但我知道我花了很多时间试图让它发挥作用。归根结底,我不得不使用的方法确实效率不高(大量的字符串转换)。我仍然不明白为什么 Mongo 觉得有必要推出自己的 json 工厂......
    • 凯文戴-对不起。我在发表评论后查看了日期。我知道那种为看似微不足道的事情投入疯狂时间的感觉。我一直在尝试将 bson 转换为 json 字符串。读完这篇文章后,我得到了它。
    猜你喜欢
    • 1970-01-01
    • 2015-04-07
    • 1970-01-01
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-20
    相关资源
    最近更新 更多