java - 在 Jackson 中反序列化 bson long 原始 json

标签 java mongodb jackson jackson2

我使用 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:无法反序列化 START_OBJECT token 中的长实例。

查看 bson,Mongo 数据返回给我们的方式如下:

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

所以...我认为我需要为 Jackson 注册一个额外的反序列化器来处理这种情况(检查 ID_START_OBJECT 的 token 类型,解析它是否存在,否则委托(delegate)给内置反序列化器)。我尝试使用 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;
}

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

jackson 版本 2.9.3。 MongoDB 驱动程序版本 3.6。

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

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

最佳答案

我通过创建 JsonWriterSettings 对象来修复 Mongo 方面的问题来抑制奇怪的 json 反序列化,从而使其正常工作。这来自这里: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;
    }
}

关于java - 在 Jackson 中反序列化 bson long 原始 json,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47985123/

相关文章:

windows - 在 Windows 7 x64 系统上安装 jre-6u30-windows-i586.exe 时出现问题

node.js - 在 Nodejs 应用程序中集成 Google Login API

php - 如何在一个PHP页面中只查询一次数据库中的数据,然后在多个PHP页面中使用?

mongodb - 带 SSL 的 ReactiveMongo

spring - 如何在Spring Boot应用程序中配置Jackson而不覆盖纯java中的Spring默认设置

java - Lombok + Jackson => MismatchedInputException

java - Android 中特定边邻接矩阵中的 Dijkstra 算法

java - 如何在@PropertySources中引用单独的@PropertySource来获取值

java - Android TabBar 像 Iphone 问题

java - 如何让 Jackson 序列化器更加 DRY?