无法使用 Java MongoDB 驱动程序读取或序列化带有枚举的 POJO

Jos*_*nez 3 java serialization pojo mongodb

我有一个现有对象,想使用 Java + POJO 编解码器在 MongoDB 中序列化。由于某种原因,驱动程序尝试创建枚举实例而不是使用 valueOF:

org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'phase'. Failed to decode 'value'. Cannot find a public constructor for 'SimplePhaseEnumType'.
at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:192)
at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:168)
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:122)
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:126)
at com.mongodb.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
Run Code Online (Sandbox Code Playgroud)

枚举:

public enum SimplePhaseEnumType {

PROPOSED("Proposed"),
INTERIM("Interim"),
MODIFIED("Modified"),
ASSIGNED("Assigned");
private final String value;

SimplePhaseEnumType(String v) {
    value = v;
}

public String value() {
    return value;
}

public static SimplePhaseEnumType fromValue(String v) {
    for (SimplePhaseEnumType c: SimplePhaseEnumType.values()) {
        if (c.value.equals(v)) {
            return c;
        }
    }
    throw new IllegalArgumentException(v);
}}
Run Code Online (Sandbox Code Playgroud)

并且该类使用枚举(仅显示相关字段):

public class SpecificPhaseType {

protected SimplePhaseEnumType value;
protected String date;

public SimplePhaseEnumType getValue() {
    return value;
}

public void setValue(SimplePhaseEnumType value) {
    this.value = value;
}}
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种方法来注释类,以告诉驱动程序在遇到这些字段时使用不同的方法来序列化/反序列化这些字段。我知道如何在序列化/反序列化期间跳过它们,但这并不能解决问题:

public class SpecificPhaseType {

@BsonIgnore
protected SimplePhaseEnumType value;
Run Code Online (Sandbox Code Playgroud)

关于我可以在哪里查看(代码、文档)有什么帮助吗?我已经检查了PojoQuickTour.javaMongoDB 驱动程序快速入门 - POJOPOJO - 普通旧 Java 对象

谢谢!

——何塞

Jos*_*nez 5

我想出了该怎么做,您首先需要编写一个自定义编解码器来将枚举读取和写入为字符串(如果您想节省空间,序数是另一种选择,但字符串对我来说已经足够了):

package com.kodegeek.cvebrowser.persistence.serializers;

import com.kodegeek.cvebrowser.entity.SimplePhaseEnumType;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;

public class SimplePhaseEnumTypeCodec implements Codec<SimplePhaseEnumType>{
    @Override
    public SimplePhaseEnumType decode(BsonReader reader, DecoderContext decoderContext) {
        return SimplePhaseEnumType.fromValue(reader.readString());
    }

    @Override
    public void encode(BsonWriter writer, SimplePhaseEnumType value, EncoderContext encoderContext) {
        writer.writeString(value.value());
    }

    @Override
    public Class<SimplePhaseEnumType> getEncoderClass() {
        return SimplePhaseEnumType.class;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后您需要注册新的编解码器,以便 MongoDB 可以使用您的类处理枚举:

/**
 * MongoDB could not make this any simpler ;-)
 * @return a Codec registry
 */
public static CodecRegistry getCodecRegistry() {
    final CodecRegistry defaultCodecRegistry = MongoClient.getDefaultCodecRegistry();
    final CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(packages).build();
    final CodecRegistry cvePojoCodecRegistry = CodecRegistries.fromProviders(pojoCodecProvider);
    final CodecRegistry customEnumCodecs = CodecRegistries.fromCodecs(
            new SimplePhaseEnumTypeCodec(),
            new StatusEnumTypeCodec(),
            new TypeEnumTypeCodec()
    );
    return CodecRegistries.fromRegistries(defaultCodecRegistry, customEnumCodecs, cvePojoCodecRegistry);
}
Run Code Online (Sandbox Code Playgroud)

Jackson 可以更轻松地使用 @JsonSerializer / @JsonDeserializer 等注释来注册自定义序列化器/反序列化器,而 Mongo 则强制您处理注册表。没什么大不了的 :-)

您可以在此处查看完整的源代码。希望这可以为必须处理类似问题的任何人节省一些时间。

  • 这不再适用于 MongoDB 驱动程序 3.8.x。然而,驱动程序版本 3.8.2 提供了开箱即用的此功能(https://github.com/mongodb/mongo-java-driver/commit/befbea5c0a695e35e8789bd9ddbc4dabf2832176),无需自定义编解码器。 (3认同)