在jackson

时间:2017-12-27 00:45:55

标签: 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检索一个长度大于Integer.MAXVALUE的文档。发生这种情况时,反序列化失败,并显示以下消息:

引起:com.fasterxml.jackson.databind.JsonMappingException:无法反序列化START_OBJECT标记中的long实例。

关注bson,以下是Mongo数据如何回复给我们:

&#34;邮戳&#34; :{&#34; $ numberLong&#34; :&#34; 1514334498165&#34; }

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

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调用(原语处理方式不同,可能会使注册的反序列化器短路吗?)。

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

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

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

1 个答案:

答案 0 :(得分:2)

我通过创建一个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;
    }
}