编解码器
概述
在本指南中,您可以了解 编解码器 以及用于在 MongoDB Java 驱动程序中将 Java 对象编码和解码为 BSON 数据的支持类。以下Codec
抽象允许您将任何 Java 类型映射到相应的 BSON 类型。您可以使用此功能直接将域对象映射到 BSON,而无需使用中间的基于映射的对象,如 Document
或 BsonDocument
。
您可以在以下部分了解如何使用 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> { public void encode(BsonWriter writer, PowerStatus value, EncoderContext encoderContext) { if (value != null) { writer.writeBoolean(value.equals(PowerStatus.ON) ? Boolean.TRUE : Boolean.FALSE); } } public PowerStatus decode(BsonReader reader, DecoderContext decoderContext) { return reader.readBoolean() ? PowerStatus.ON : PowerStatus.OFF; } public Class<PowerStatus> getEncoderClass() { return PowerStatus.class; } }
您可以将PowerStatusCodec
的实例添加到您的CodecRegistry
中,该CodecRegistry
包含您的Codec
与它所应用的Java对象类型之间的映射。有关如何包含您的Codec
,请参阅此页面的CodecRegistry
部分。
有关本节中类和接口的更多信息,请参阅以下API文档
CodecRegistry
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
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() {} 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
BsonTypeClassMap
类包含BSON和Java类型之间推荐的映射。您可以在自定义的Codec
或CodecProvider
中使用此类,以帮助您管理将哪些Java类型解码为实现Iterable
或Map
的容器类,例如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
的示例,请参阅以下类的驱动程序源代码
自定义编解码器示例
在本节中,我们展示了如何实现 Codec
和 CodecProvider
来定义自定义 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 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 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 public Class<Monolight> getEncoderClass() { return Monolight.class; } }
为了确保我们为 Monolight
的字段创建了 Codec
实例,我们实现了以下代码示例中的自定义 CodecProvider
。
public class MonolightCodecProvider implements CodecProvider { public MonolightCodecProvider() {} 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文档