文档菜单
文档首页
/ / /
Java 同步驱动
/ /

编解码器

本页内容

  • 概述
  • 编解码器
  • 编解码器注册表
  • 编解码器提供者
  • 默认编解码器注册表
  • BsonTypeClassMap
  • 自定义编解码器示例

在本指南中,您可以了解 编解码器 以及用于在 MongoDB Java 驱动程序中将 Java 对象编码和解码为 BSON 数据的支持类。以下Codec 抽象允许您将任何 Java 类型映射到相应的 BSON 类型。您可以使用此功能直接将域对象映射到 BSON,而无需使用中间的基于映射的对象,如 DocumentBsonDocument

您可以在以下部分了解如何使用 Codec 抽象指定自定义编码和解码逻辑,并查看示例实现。

  • 编解码器

  • 编解码器注册表

  • 编解码器提供者

  • 自定义编解码器示例

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

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

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

encode() 方法需要以下参数

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

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

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

参数类型
描述
bsonReader
实现了BsonReader接口的类的实例,该接口公开了读取BSON文档的方法。例如,BsonBinaryReader实现从二进制数据流中读取。
decoderContext
包含解码为Java对象时的BSON数据信息。

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

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

PowerStatus枚举包含“ON”和“OFF”值,以表示电源开关的状态。

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中,该CodecRegistry包含您的Codec与它所应用的Java对象类型之间的映射。有关如何包含您的Codec,请参阅此页面的CodecRegistry部分。

有关本节中类和接口的更多信息,请参阅以下API文档

CodecRegistry是一个不可变的Codec实例集合,它们编码和解码它们指定的Java类。您可以使用以下任何CodecRegistries类静态工厂方法从关联类型中包含的Codec实例构建一个CodecRegistry

  • fromCodecs()

  • fromProviders()

  • fromRegistries()

以下代码片段展示了如何使用fromCodecs()方法构建一个CodecRegistry

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

在前面的示例中,我们将以下Codec实现分配给CodecRegistry

  • IntegerCodec,一个将整数转换为BSON的Codec,是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,以便将MonolightCodec所需的任何Codec实例传递给先前的示例中的CodecRegistry实例,例如PowerStatusCodec

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类的读写操作的示例,请参阅本指南中的Custom Codec Example部分。

当处理POJO时,请考虑使用PojoCodecProvider以最小化重复代码,并转换常用数据类型并自定义它们的行为。有关更多信息,请参阅我们的POJO Customization指南

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

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

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

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

BsonTypeClassMap类包含BSON和Java类型之间推荐的映射。您可以在自定义的CodecCodecProvider中使用此类,以帮助您管理将哪些Java类型解码为实现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;
}
}

为了确保我们为 Monolight 的字段创建了 Codec 实例,我们实现了以下代码示例中的自定义 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实例

以下示例类包含代码,通过传递给withCodecRegistry()方法,将MonolightCodecProvider分配给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自定义