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

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

在本指南中,您可以学习;了解编解码器和支持类,它们在Java Reactive Streams驾驶员中处理Java对象与BSON数据之间的编码和解码。Codec 抽象类允许您将任何Java类型映射到相应的BSON类型。您可以使用此抽象将域对象直接映射到BSONDocument ,或从 BSON 直接映射到域对象,而无需使用 或BsonDocument 等基于中间映射的对象。

在以下部分中,了解如何使用 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对象类型之间的映射。继续访问本页的 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实例中所需的任何 Codec 实例传递给 BookCodec,例如上示例中的 ReadStatusCodec

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 类型解码为实现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 的示例,请参阅 DocumentCodecProvider DocumentCodec 类的驾驶员源代码。

本部分介绍如何实现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文档: