编码对象时未使用MongoDB BSON编解码器 [英] MongoDB BSON codec not being used while encoding object
问题描述
我正在尝试将对象存储在 MongoDB 数据库中(使用 MongoDB 3.0.2)并且在尝试使用错误消息对对象进行编码时收到 CodecConfigurationException
I'm attempting to store an object in a MongoDB database (using MongoDB 3.0.2) and am getting a CodecConfigurationException
when attempting to encode the object with error message
Can't find a codec for class java.time.LocalDate.
我已经为 LocalDate
对象编写并包含了一个编解码器.详情如下.
I have written and included a codec for the LocalDate
objects. Details follow.
我试图存储的对象 DutyBlock
具有以下成员变量:
The object, DutyBlock
, that I'm attempting to store has these member variables:
public class DutyBlock {
private LocalDate startDate;
private LocalDate endDate; //Inclusive
private int blockLength;
private double pointValue;
private ArrayList<Ra> assigned;
}
我编写了以下编解码器来对数据库中的 DutyBlock
对象进行编码:
I wrote the following codec to encode the DutyBlock
objects within the database:
public class DutyBlockCodec implements Codec<DutyBlock> {
@Override
public void encode(BsonWriter writer, DutyBlock t, EncoderContext ec) {
Document document = new Document();
document.append("startDate", t.getStartDate());
document.append("endDate", t.getEndDate());
document.append("blockLength", t.getBlockLength());
document.append("pointValue", t.getPointValue());
document.append("assigned", t.getRasOnDuty());
writer.writeString(document.toJson()); //Line 27 in the error message.
}
@Override
public Class<DutyBlock> getEncoderClass() {
return DutyBlock.class;
}
@Override
public DutyBlock decode(BsonReader reader, DecoderContext dc) {
String json = reader.readString();
return new DutyBlock(Document.parse(json));
}
}
由于 MongoDB 当前不支持 java.time.LocalDate 类
,我编写了以下编解码器来对数据库中的 LocalDate
对象进行编码:
Since MongoDB currently does not support the java.time.LocalDate class
, I've written the following codec to encode the LocalDate
objects within the database:
public class LocalDateCodec implements Codec<LocalDate> {
@Override
public void encode(BsonWriter writer, LocalDate t, EncoderContext ec) {
writer.writeString(t.toString());
}
@Override
public Class<LocalDate> getEncoderClass() {
return LocalDate.class;
}
@Override
public LocalDate decode(BsonReader reader, DecoderContext dc) {
String date = reader.readString();
return LocalDate.parse(date);
}
}
我在 MongoClient 级别将两个 Codec
(以及一个用于 Ra
类型)添加到 CodecRegistry
实例化 MongoClient.
I've added the two Codec
's (along with one for the Ra
type) to the CodecRegistry
at the MongoClient level while instantiating the MongoClient.
public class DutyScheduleDB {
private MongoClient mongoClient;
private MongoDatabase db;
public DutyScheduleDB() {
CodecRegistry codecRegistry =
CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(new LocalDateCodec(), new DutyBlockCodec(), new RaCodec()),
MongoClient.getDefaultCodecRegistry());
MongoClientOptions options = MongoClientOptions.builder()
.codecRegistry(codecRegistry).build();
mongoClient = new MongoClient(new ServerAddress(), options);
db = mongoClient.getDatabase("DutySchedulerDB");
}
. (More code not shown)
.
.
}
我尝试将 DutyBlock
对象的 ArrayList
作为 org.bson.Document
的一部分存储在 MongoDB 数据库中.
I attempt to store an ArrayList
of DutyBlock
objects as part of a org.bson.Document
within the MongoDB database.
public void storeScheduledCalendar(String id,
String calendarName,
ArrayList<DutyBlock> cal) {
//Access collection of scheduled calendars.
MongoCollection collection = db.getCollection("ScheduledCalendars");
//Query parameter is uuid + calendarName.
Document doc = new Document("name", id + calendarName);
doc.append("dutyBlocks", cal);
//Insert doc to collection.
collection.insertOne(doc); //Line 59 in the error message.
}
但是,我遇到了此错误消息:
However, I'm running into this error message:
Exception in thread "main" org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.time.LocalDate.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:174)
at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:189)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:131)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45)
at org.bson.Document.toJson(Document.java:294)
at org.bson.Document.toJson(Document.java:268)
at org.bson.Document.toJson(Document.java:255)
at SchedulingHeuristic.DutyBlockCodec.encode(DutyBlockCodec.java:27)
at SchedulingHeuristic.DutyBlockCodec.encode(DutyBlockCodec.java:16)
at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91)
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:175)
at org.bson.codecs.DocumentCodec.writeIterable(DocumentCodec.java:197)
at org.bson.codecs.DocumentCodec.writeValue(DocumentCodec.java:170)
at org.bson.codecs.DocumentCodec.writeMap(DocumentCodec.java:189)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:131)
at org.bson.codecs.DocumentCodec.encode(DocumentCodec.java:45)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
at com.mongodb.connection.InsertCommandMessage.writeTheWrites(InsertCommandMessage.java:99)
at com.mongodb.connection.InsertCommandMessage.writeTheWrites(InsertCommandMessage.java:43)
at com.mongodb.connection.BaseWriteCommandMessage.encodeMessageBody(BaseWriteCommandMessage.java:112)
at com.mongodb.connection.BaseWriteCommandMessage.encodeMessageBody(BaseWriteCommandMessage.java:35)
at com.mongodb.connection.RequestMessage.encode(RequestMessage.java:132)
at com.mongodb.connection.BaseWriteCommandMessage.encode(BaseWriteCommandMessage.java:89)
at com.mongodb.connection.WriteCommandProtocol.sendMessage(WriteCommandProtocol.java:170)
at com.mongodb.connection.WriteCommandProtocol.execute(WriteCommandProtocol.java:73)
at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:66)
at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:37)
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:155)
at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:219)
at com.mongodb.connection.DefaultServerConnection.insertCommand(DefaultServerConnection.java:108)
at com.mongodb.operation.MixedBulkWriteOperation$Run$2.executeWriteCommandProtocol(MixedBulkWriteOperation.java:416)
at com.mongodb.operation.MixedBulkWriteOperation$Run$RunExecutor.execute(MixedBulkWriteOperation.java:604)
at com.mongodb.operation.MixedBulkWriteOperation$Run.execute(MixedBulkWriteOperation.java:363)
at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:148)
at com.mongodb.operation.MixedBulkWriteOperation$1.call(MixedBulkWriteOperation.java:141)
at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:186)
at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:177)
at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:141)
at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:72)
at com.mongodb.Mongo.execute(Mongo.java:747)
at com.mongodb.Mongo$2.execute(Mongo.java:730)
at com.mongodb.MongoCollectionImpl.executeSingleWriteRequest(MongoCollectionImpl.java:482)
at com.mongodb.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:277)
at DutyScheduleDB.storeScheduledCalendar(DutyScheduleDB.java:59)
at DutyScheduleDB.main(DutyScheduleDB.java:106)
在尝试对 DutyBlock
对象进行编码时,似乎没有使用我的 LocalDate
编解码器,尽管我已经验证了我尝试存储的集合org.bson.Document
确实通过
It seems that my codec for LocalDate
isn't being used when attempting to encode DutyBlock
objects, though I've verified that the collection that I am attempting to store the org.bson.Document
in does indeed contain the LocalDateCodec
via a
System.out.println(collection.getCodecRegistry().get(LocalDate.class));
谁能提供一些有关为什么会发生这种情况的见解?
Can anyone can provide some insight on why this is happening?
推荐答案
经过几天的研究,我找到了解决方案.
After several days of research, I've figured out a solution.
DutyBlockCodec
依赖于 LocalDateCodec
(我创建的)来编码/解码.仅通过将两个编解码器添加到同一个编解码器注册表中并不能满足这种依赖性.解决方案是传递一个 CodecRegistry
对象,其中包含 DutyBlockCodec
所依赖的编解码器(例如,一个 CodecRegistry
在其中包含 LocalDateCodec
code>) 到 DutyBlockCodec
的构造函数,该构造函数存储为成员变量.为了使用LocalDateCodec
进行编码,我使用了EncoderContext.encodeWithChildContext()
方法,传入编解码器、编写器和元素进行编码.此外,我编写单独的字段,而不是将 Document
编写为 String
(如在我的原始代码中).因此 DutyBlock
编解码器最终看起来像这样:
The DutyBlockCodec
depends on the LocalDateCodec
(which I created) in order to encode/decode. This dependency isn't satisfied just by adding the two codecs into the same codec registry. The solution is to pass a CodecRegistry
object containing the codecs that DutyBlockCodec
depends on (e.g. a CodecRegistry
containing within it the LocalDateCodec
) to the DutyBlockCodec
's constructor, which is stored as a member variable. In order to use the LocalDateCodec
to encode, I use the EncoderContext.encodeWithChildContext()
method, passing in the codec, writer, and element to encode. Additionally, I write individual fields rather than writing a Document
as a String
(as in my original code). Thus the DutyBlock
codec ends up looking like this:
public class DutyBlockCodec implements Codec<DutyBlock> {
private final CodecRegistry codecRegistry;
public DutyBlockCodec(final CodecRegistry codecRegistry) {
this.codecRegistry = codecRegistry;
}
@Override
public void encode(BsonWriter writer, DutyBlock t, EncoderContext ec) {
writer.writeStartDocument();
Codec dateCodec = codecRegistry.get(LocalDate.class);
writer.writeName("startDate");
ec.encodeWithChildContext(dateCodec, writer, t.getStartDate());
writer.writeName("endDate");
ec.encodeWithChildContext(dateCodec, writer, t.getEndDate());
writer.writeName("blockLength");
writer.writeInt32(t.getBlockLength());
writer.writeName("pointValue");
writer.writeDouble(t.getPointValue());
//Writing ArrayList of RAs
writer.writeName("assigned");
writer.writeStartArray();
for (Ra ra : t.getRasOnDuty()) {
Codec raCodec = codecRegistry.get(Ra.class);
ec.encodeWithChildContext(raCodec, writer, ra);
}
writer.writeEndArray();
writer.writeEndDocument();
}
@Override
public Class<DutyBlock> getEncoderClass() {
return DutyBlock.class;
}
@Override
public DutyBlock decode(BsonReader reader, DecoderContext dc) {
reader.readStartDocument();
Codec<LocalDate> dateCodec = codecRegistry.get(LocalDate.class);
reader.readName();
LocalDate startDate = dateCodec.decode(reader, dc);
reader.readName();
LocalDate endDate = dateCodec.decode(reader, dc);
reader.readName();
int blockLength = reader.readInt32();
reader.readName();
double pointValue = reader.readDouble();
//Reading ArrayList of RAs
reader.readName();
Codec<Ra> raCodec = codecRegistry.get(Ra.class);
ArrayList<Ra> rasOnDuty = new ArrayList<>();
reader.readStartArray();
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
rasOnDuty.add(raCodec.decode(reader, dc));
}
reader.readEndArray();
reader.readEndDocument();
return new DutyBlock(startDate, endDate, blockLength, pointValue, rasOnDuty);
}
}
DutyBlockCodec
依赖于另一个编解码器,因此需要在其构造函数中传入 CodecRegistry
.虽然我相信可以使用 LocalDateCodec
创建一个 CodecRegistry
,然后将其作为参数传递给 DutyBlockCodec
的构造函数,然后创建另一个CodecRegistry
包含LocalDateCodec
和DutyBlockCodec
,这个比较混乱,MongoDB 提供了一个功能,CodecProvider
来方便这个过程.
DutyBlockCodec
depends on another codec, and so requires a CodecRegistry
to be passed in on its constructor. While I believe it is possible to create a CodecRegistry
with the LocalDateCodec
, then pass this as an argument to DutyBlockCodec
's constructor, then create another CodecRegistry
containing both LocalDateCodec
and DutyBlockCodec
, this is rather confusing, and MongoDB provides a functionality, the CodecProvider
to facilitate this process.
使用CodecProvider
接口,我写了一个DutyBlockCodecProvider
public class DutyBlockCodecProvider implements CodecProvider {
@Override
public <T> Codec<T> get(Class<T> type, CodecRegistry cr) {
if (type == DutyBlock.class) {
return (Codec<T>) new DutyBlockCodec(cr);
}
return null;
}
}
我使用 CodecRegistries.fromProviders()
方法将这些 CodecProviders
添加到 MongoDB 客户端.
I added these CodecProviders
to the MongoDB Client using the CodecRegistries.fromProviders()
method.
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
CodecRegistries.fromCodecs(new LocalDateCodec()),
CodecRegistries.fromProviders(
new RaCodecProvider(),
new DutyBlockCodecProvider(),
new ScheduledDutyCodecProvider()),
MongoClient.getDefaultCodecRegistry());
MongoClientOptions options = MongoClientOptions.builder()
.codecRegistry(codecRegistry).build();
mongoClient = new MongoClient(new ServerAddress(), options);
db = mongoClient.getDatabase("DutySchedulerDB");
我的这个项目的源代码可以在https://github.com/desrepair/DutyScheduler我愿意回答人们可能提出的任何问题.
My source code for this project can be found at https://github.com/desrepair/DutyScheduler I'm open to answering any questions people may have.
这篇关于编码对象时未使用MongoDB BSON编解码器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!