对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

使用类型编解码器对数据进行编码

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

在以下部分中了解如何使用 Codec 抽象类来指定自定义编码和解码逻辑:

要了解如何自定义普通旧式 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

ReadStatus枚举包含值READUNREAD,用以表示书籍是否已阅读。

public enum ReadStatus {
READ,
UNREAD
}

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

public class ReadStatusCodec implements Codec<ReadStatus> {
@Override
public void encode(BsonWriter writer, ReadStatus value, EncoderContext encoderContext) {
writer.writeBoolean(value.equals(ReadStatus.READ) ? Boolean.TRUE : Boolean.FALSE);
}
@Override
public ReadStatus decode(BsonReader reader, DecoderContext decoderContext) {
return reader.readBoolean() ? ReadStatus.READ : ReadStatus.UNREAD;
}
@Override
public Class<ReadStatus> getEncoderClass() {
return ReadStatus.class;
}
}

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

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

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

  • fromCodecs()

  • fromProviders()

  • fromRegistries()

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

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

在前面的示例中,CodecRegistry包含以下 Codec 实现:

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

  • ReadStatusCodec,我们的示例 Codec,可将 Java 枚举值转换为 BSON 布尔值。

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

Codec<ReadStatus> readStatusCodec = codecRegistry.get(ReadStatus.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 实例(例如前面示例中的 ReadStatusCodec)所需的任何 Codec 实例传递给 BookCodec

public class BookCodecProvider implements CodecProvider {
public BookCodecProvider() {}
@Override
@SuppressWarnings("unchecked")
public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
if (clazz == Book.class) {
return (Codec<T>) new BookCodec(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 types 解码为能够实现 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 的示例,请参阅 DocumentCodecProviderDocumentCodec 类的驱动程序源代码。

本节介绍如何实现 CodecCodecProvider 来定义自定义 Java 类的编码和解码逻辑。它还展示如何指定和使用自定义实现来执行插入和检索操作。

以下示例定义了一个名为 Book 的自定义类及其字段,用于在 MongoDB 集合中存储和检索:

public class Book {
private String title;
private ReadStatus readStatus = ReadStatus.UNREAD;
private Integer pageCount;
public Book() {}
// ...

此类包含以下字段,每个字段都需要 Codec

  • title 包含一个 String 值,对于该值,示例使用 BSON 库中包含的 StringCodec

  • readStatus 描述一本书是否已读,其中示例使用自定义的ReadStatusCodec将枚举值转换为 BSON 布尔值。

  • pageCount 包含一个 Integer 值,对于该值,示例使用 BSON 库中包含的 IntegerCodec

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

public class BookCodec implements Codec<Book> {
private Codec<String> stringCodec;
private Codec<ReadStatus> readStatusCodec;
private Codec<Integer> integerCodec;
public BookCodec(CodecRegistry registry) {
this.stringCodec = registry.get(String.class);
this.readStatusCodec = registry.get(ReadStatus.class);
this.integerCodec = registry.get(Integer.class);
}
// Defines an encode() method to convert Book field values to BSON values
@Override
public void encode(BsonWriter writer, Book value, EncoderContext encoderContext) {
writer.writeStartDocument();
writer.writeName("title");
stringCodec.encode(writer, value.getTitle(), encoderContext);
writer.writeName("readStatus");
readStatusCodec.encode(writer, value.getReadStatus(), encoderContext);
writer.writeName("pageCount");
integerCodec.encode(writer, value.getPageCount(), encoderContext);
writer.writeEndDocument();
}
// Defines a decode() method to convert BSON values to Book field values
@Override
public Book decode(BsonReader reader, DecoderContext decoderContext) {
Book book = new Book();
reader.readStartDocument();
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
String fieldName = reader.readName();
if (fieldName.equals("title")) {
book.setTitle(stringCodec.decode(reader, decoderContext));
} else if (fieldName.equals("readStatus")) {
book.setReadStatus(readStatusCodec.decode(reader, decoderContext));
} else if (fieldName.equals("pageCount")) {
book.setPageCount(integerCodec.decode(reader, decoderContext));
} else if (fieldName.equals("_id")) {
reader.readObjectId();
} else {
reader.skipValue();
}
}
reader.readEndDocument();
return book;
}
// Returns an instance of the Book class, since Java cannot infer the class type
@Override
public Class<Book> getEncoderClass() {
return Book.class;
}
}

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

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

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

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

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

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

public class BookCodecExample {
public static void main(String[] args) {
String uri = "<MongoDB connection URI>";
try (MongoClient mongoClient = MongoClients.create(uri)) {
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(new ReadStatusCodec()),
CodecRegistries.fromProviders(new BookCodecProvider()),
MongoClientSettings.getDefaultCodecRegistry());
MongoDatabase database = mongoClient.getDatabase("codecs_example_db");
MongoCollection<Book> collection = database.getCollection("books", Book.class)
.withCodecRegistry(codecRegistry);
// construct and insert an instance of Book
Book myBook = new Book();
myBook.setTitle("The Hobbit");
myBook.setReadStatus(ReadStatus.READ);
myBook.setPageCount(310);
Mono.from(collection.insertOne(myBook)).block();
// retrieve one or more instances of Book
Flux.from(collection.find())
.doOnNext(System.out::println)
.blockLast();
}
}
}

上述示例的输出如下所示:

Book [title=The Hobbit, readStatus=READ, pageCount=310]

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