Docs 菜单
Docs 主页
/ / /
Java (Sync) 驱动程序
/ /

编解码器

在此页面上

  • Overview
  • 编解码器
  • CodecRegistry
  • CodecProvider
  • 默认编解码器注册表
  • BsonTypeClassMap
  • 自定义编解码器示例

在本指南中,可以了解编解码器和支持类,这些类在 MongoDB Java 驱动程序中处理 Java 对象与 BSON 数据之间的编码和解码。使用 Codec 抽象类,可将任何 Java 类型映射到相应的 BSON 类型。可以使用此类将域对象直接映射到 BSON,或从 BSON 直接映射到域对象,而无需使用 DocumentBsonDocument 等基于中间映射的对象。

可以了解如何使用 Codec 抽象类来指定自定义编码和解码逻辑,并在以下部分中查看示例实施:

  • 编解码器

  • CodecRegistry

  • CodecProvider

  • 自定义编解码器示例

如果您要为普通的旧 Java 对象 (POJO) 自定义编码和解码逻辑,请阅读我们的 POJO 自定义指南。

Codec 接口包含用于将 Java 对象序列化和反序列化为 BSON 数据的抽象方法。您可以在该接口的实现中定义 BSON 和 Java 对象之间的转换逻辑。

要实现 Codec 接口,请覆盖 encode()decode()getEncoderClass() 抽象方法。

encode() 方法需要使用以下参数:

Parameter Type
说明
writer
实施 BsonWriter 的类的实例,该接口类型公开用于编写 BSON 文档的方法。例如,BsonBinaryWriter 实施写入二进制数据流。使用此实例通过适当的写入方法写入 BSON 值。
value
实施所编码的数据。该类型必须与分配给您的实施的类型变量相匹配。
encoderContext
包含编码为 BSON 的 Java 对象数据的相关元信息,包括是否将当前值存储在 MongoDB 集合中。

此方法使用 BsonWriter 实例将编码值发送到 MongoDB,并且不返回值。

decode() 方法返回使用 BSON 数据中的值填充的 Java 对象实例。此方法需要以下参数:

Parameter Type
说明
bsonReader
实现 BsonReader 的类的实例,该接口类型公开用于读取 BSON 文档的方法。例如,BsonBinaryReader 实施从二进制数据流中读取。
decoderContext
包含有关解码为 Java 对象的 BSON 数据的信息。

getEncoderClass() 方法返回该 Java 类的一个类实例,因为 Java 由于类型擦除而无法推断类型。

请参阅以下代码示例,这些示例展示了如何实施自定义 Codec

PowerStatus 枚举包含值“打开”和“关闭”来表示电气开关的状态。

public enum PowerStatus {
ON,
OFF
}

PowerStatusCodec 类实现 Codec,以便将 Java enum 值转换为相应的 BSON 布尔值。encode() 方法将 PowerStatus 转换为 BSON 布尔值,decode() 方法则执行相反方向的转换。

public class PowerStatusCodec implements Codec<PowerStatus> {
@Override
public void encode(BsonWriter writer, PowerStatus value, EncoderContext encoderContext) {
if (value != null) {
writer.writeBoolean(value.equals(PowerStatus.ON) ? Boolean.TRUE : Boolean.FALSE);
}
}
@Override
public PowerStatus decode(BsonReader reader, DecoderContext decoderContext) {
return reader.readBoolean() ? PowerStatus.ON : PowerStatus.OFF;
}
@Override
public Class<PowerStatus> getEncoderClass() {
return PowerStatus.class;
}
}

您可以将 PowerStatusCodec 实例添加到 CodecRegistry,其中包含 Codec 与其所应用的 Java 对象类型之间的映射。继续访问本页的 CodecRegistry 部分,看看如何包含您的 Codec

有关此部分中的类和接口的更多信息,请参阅以下 API 文档:

CodecRegistryCodec 实例的不可变集合,这些实例对其指定的 Java 类进行编码和解码。您可以使用以下任一 CodecRegistries 类静态工厂方法,从关联类型中包含的 Codec 实例构造 CodecRegistry

  • fromCodecs()

  • fromProviders()

  • fromRegistries()

以下代码片段演示如何使用 fromCodecs() 方法构造 CodecRegistry

CodecRegistry codecRegistry = CodecRegistries.fromCodecs(new IntegerCodec(), new PowerStatusCodec());

在前面的示例中,我们为 CodecRegistry 分配以下 Codec 实施:

  • IntegerCodec,一个用于转换 IntegersCodec,并且是 BSON 软件包的一部分。

  • PowerStatusCodec,我们的示例 Codec 用于将某些 Java 字符串转换为 BSON 布尔值。

您可以使用以下代码从上一示例中的 CodecRegistry 实例中检索 Codec 实例:

Codec<String> powerStatusCodec = codecRegistry.get(String.class);
Codec<Integer> integerCodec = codecRegistry.get(Integer.class);

如果您尝试检索未注册的类的 Codec 实例,则 get() 方法会引发 CodecConfigurationException 异常。

有关此部分中的类和接口的更多信息,请参阅以下 API 文档:

CodecProvider 是接口,其中包含创建 Codec 实例并将其分配给 CodecRegistry 实例的抽象方法。与 CodecRegistry 类似,BSON 库使用 get() 方法检索的 Codec 实例在 Java 和 BSON 数据类型之间进行转换。

但是,如果你添加的类包含需要对应 Codec 对象的字段,则需要确保在实例化该类的 Codec 之前为类字段实例化 Codec 对象。你可以使用 get() 方法中的 CodecRegistry 参数传递 Codec 依赖的任何 Codec 实例。

以下代码示例展示了如何实现 CodecProvider 以将其在CodecRegistry 实例(例如前面示例中的 PowerStatusCodec)所需的任何 Codec 实例传递给MonolightCodec

public class MonolightCodecProvider implements CodecProvider {
public MonolightCodecProvider() {}
@Override
@SuppressWarnings("unchecked")
public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
if (clazz == Monolight.class) {
return (Codec<T>) new MonolightCodec(registry);
}
// return null when not a provider for the requested class
return null;
}
}

要查看演示使用这些 Codec 类进行读写操作的可运行示例,请参阅本指南的自定义编解码器示例一节。

使用 POJO 时,请考虑使用 PojoCodecProvider 最大限度地减少重复代码,以转换常用数据类型并自定义其行为。有关更多信息,请参阅我们的“POJO 定制”指南

默认编解码器注册表是一组 CodecProvider 类,用于指定常用 Java 和 MongoDB 类型之间的转换。除非您指定不同的注册表,否则驱动程序会自动使用默认的编解码器注册表。

如果你需要覆盖一个或多个 Codec 类的行为,但保留其他类的默认编解码器注册表中的行为,则可以按优先顺序指定所有注册表。例如,假设你想要使用自定义 MyEnumCodec 替换枚举类型的 Codec 的默认提供商行为,则必须将其添加到默认编解码器注册表之前的注册表列表中,如以下示例所示:

CodecRegistry newRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(new MyEnumCodec()),
MongoClientSettings.getDefaultCodecRegistry());

有关此部分中的类和接口的更多信息,请参阅以下 API 文档部分:

BsonTypeClassMap 类包含 BSON 和 Java 类型之间的推荐映射。可以在自定义 CodecCodecProvider 中使用此类来帮助管理哪些 Java 类型可将 BSON 类型解码为能够实现 IterableMap 的容器类,例如 Document 类。

您可以通过传递包含新条目或替换条目的 Map 来添加或修改 BsonTypeClassMap 默认映射。

以下代码片段显示了如何检索与默认 BsonTypeClassMap 实例中的 BSON 类型相对应的 Java 类类型:

BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap();
Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY);
System.out.println("Java type: " + clazz.getName());

此代码输出以下内容:

Java type: java.util.List

可以通过在 BsonTypeClassMap 构造函数中指定替换项来修改实例中的这些映射。以下代码段展示了如何将 BsonTypeClassMap 实例中的 ARRAY 的映射替换为 Set 类:

Map<BsonType, Class<?>> replacements = new HashMap<BsonType, Class<?>>();
replacements.put(BsonType.ARRAY, Set.class);
BsonTypeClassMap bsonTypeClassMap = new BsonTypeClassMap(replacements);
Class<?> clazz = bsonTypeClassMap.get(BsonType.ARRAY);
System.out.println("Java type: " + clazz.getName());

此代码输出以下内容:

Java type: java.util.Set

请参阅 BsonTypeClassMap API 文档,获取默认映射的完整列表。

有关 Document 类如何使用 BsonTypeClassMap 的示例,请参阅以下类的驱动程序源代码:

在本部分中,我们将展示如何实施 CodecCodecProvider 来定义自定义 Java 类的编码和解码逻辑。我们还展示了如何指定和使用自定义实施来执行插入和检索操作。

以下代码片段显示名为 Monolight 的示例自定义类以及我们要在 MongoDB 集合中存储和检索的字段:

public class Monolight {
private PowerStatus powerStatus = PowerStatus.OFF;
private Integer colorTemperature;
public Monolight() {}
// ...

此类包含以下字段,我们需要为每个字段分配一个 Codec

  • powerStatus 描述灯是“开”还是“关”,为此我们使用 PowerStatusCodec 将特定枚举值转换为 BSON 布尔值。

  • colorTemperature 描述灯光的颜色并包含一个 Integer 值,我们使用 BSON 库中包含的 IntegerCodec 对该值进行解码。

以下代码示例展示如何为 Monolight 类实施 Codec。请注意,构造函数需要一个 CodecRegistry 实例,从中检索对其字段进行编码和解码所需的 Codec 实例:

public class MonolightCodec implements Codec<Monolight>{
private Codec<PowerStatus> powerStatusCodec;
private Codec<Integer> integerCodec;
public MonolightCodec(CodecRegistry registry) {
this.powerStatusCodec = registry.get(PowerStatus.class);
this.integerCodec = registry.get(Integer.class);
}
// Defines an encode() method to convert Monolight enum values to BSON values
@Override
public void encode(BsonWriter writer, Monolight value, EncoderContext encoderContext) {
writer.writeStartDocument();
writer.writeName("powerStatus");
powerStatusCodec.encode(writer, value.getPowerStatus(), encoderContext);
writer.writeName("colorTemperature");
integerCodec.encode(writer, value.getColorTemperature(), encoderContext);
writer.writeEndDocument();
}
// Defines a decode() method to convert BSON values to Monolight enum values
@Override
public Monolight decode(BsonReader reader, DecoderContext decoderContext) {
Monolight monolight = new Monolight();
reader.readStartDocument();
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
String fieldName = reader.readName();
if (fieldName.equals("powerStatus")) {
monolight.setPowerStatus(powerStatusCodec.decode(reader, decoderContext));
} else if (fieldName.equals("colorTemperature")) {
monolight.setColorTemperature(integerCodec.decode(reader, decoderContext));
} else if (fieldName.equals("_id")){
reader.readObjectId();
}
}
reader.readEndDocument();
return monolight;
}
// Returns an instance of the Monolight class, since Java cannot infer the class type
@Override
public Class<Monolight> getEncoderClass() {
return Monolight.class;
}
}

为了确保字段的 Codec 实例可用于 Monolight,我们实施了以下代码示例中所示的自定义 CodecProvider

public class MonolightCodecProvider implements CodecProvider {
public MonolightCodecProvider() {}
@Override
@SuppressWarnings("unchecked")
public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
if (clazz == Monolight.class) {
return (Codec<T>) new MonolightCodec(registry);
}
// return null when not a provider for the requested class
return null;
}
}

定义转换逻辑后,我们可以执行以下操作:

  • 将来自 Monolight 实例的数据存储到 MongoDB 中

  • 将 MongoDB 中的数据提到实例 Monolight

以下示例类中包含的代码通过将 MonolightCodecProvider 传递给 withCodecRegistry() 方法,从而将其赋值给 MongoCollection 实例。该示例类还使用 Monolight 类和关联的编解码器插入和检索数据:

public class MonolightCodecExample {
public static void main(String[] args) {
String uri = "<MongoDB connection URI>";
try (MongoClient mongoClient = MongoClients.create(uri)) {
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(new IntegerCodec(), new PowerStatusCodec()),
CodecRegistries.fromProviders(new MonolightCodecProvider()),
MongoClientSettings.getDefaultCodecRegistry());
MongoDatabase database = mongoClient.getDatabase("codecs_example_products");
MongoCollection<Monolight> collection = database.getCollection("monolights", Monolight.class).withCodecRegistry(codecRegistry);
// construct and insert an instance of Monolight
Monolight myMonolight = new Monolight();
myMonolight.setPowerStatus(PowerStatus.ON);
myMonolight.setColorTemperature(5200);
collection.insertOne(myMonolight);
// retrieve one or more instances of Monolight
List<Monolight> lights = new ArrayList<>();
collection.find().into(lights);
System.out.println(lights);
}
}
}

运行前一示例应能看到以下输出:

[Monolight [powerStatus=ON, colorTemperature=5200]]

有关本节中提到的方法和类的详情,请参阅以下 API 文档:

后退

POJO 自定义