英文:
Mongodb polymorphism Java object unable to determine concrete class
问题
我正在使用Micronaut与Mongodb,并且在读取多态Java对象时遇到错误,因为Mongodb无法通过其类型确定具体的实现类。
myPet: { "_id" : ObjectId("5f96f4633dbd690c548c2a38"), pets: [{"type" : "Cat" }, {"type" : "Dog" }]}
public class MyPet {
private List<Animal> pets = new ArrayList<Animal>(0);
public List<Animal> getPets() {
return pets;
}
public void setPets(List<Animal> pets) {
this.pets = pets;
}
}
public class Cat implements Animal {
private String type = "Cat";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public class Dog implements Animal {
private String type = "Dog";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public interface Animal {
String getType();
}
我尝试了以下代码:
MongoCollection<MyPet> myPetColl = db.getCollection("myPet", MyPet.class);
myPetColl.find()
但是出现以下错误:
意外错误:使用AutomaticPojoCodec进行解码时发生异常。
解码为'MyPet'失败,异常如下:
解码'content'时出错:找不到'Animal'的公共构造函数。
可能需要显式配置和注册自定义的Codec或PojoCodec来处理此类型。
org.bson.codecs.configuration.CodecConfigurationException:使用AutomaticPojoCodec进行解码时发生异常。
解码为'MyPet'失败,异常如下:
解码'content'时出错:找不到'Animal'的公共构造函数。
可能需要显式配置和注册自定义的Codec或PojoCodec来处理此类型。
at org.bson.codecs.pojo.AutomaticPojoCodec.decode(AutomaticPojoCodec.java:40)
at com.mongodb.internal.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
...
是否有办法使用if条件返回正确的类类型:
如果 (obj.type == "Dog") 返回 Dog.class,否则返回 Cat.class
就像在Jackson中一样:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "Dog"),
@JsonSubTypes.Type(value = Cat.class, name = "Cat"),
})
英文:
I am using Mongodb with Micronaut and reading polymorphic Java object gives me error because Mongodb is unable to determine the concrete implementation class by its type.
myPet: { "_id" : ObjectId("5f96f4633dbd690c548c2a38"), pets: [{"type" : "Cat" }, {"type" : "Dog" }]}
public class MyPet {
private List<Animal> pets = new ArrayList<Animal>(0);
public List<Animal> getPets() {
return pets;
}
public void setPets(List<Animal> pets) {
this.pets = pets;
}
}
public class Cat implements Animal {
private String type = "Cat";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public class Dog implements Animal {
private String type = "Dog";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public interface Animal {
String getType();
}
I tried
MongoCollection<MyPet> myPetColl = db.getCollection("myPet", MyPet.class);
myPetColl.find()
but get below error
Unexpected error occurred: An exception occurred when decoding using the AutomaticPojoCodec.
Decoding into a 'MyPet' failed with the following exception:
Failed to decode 'MyPet'. Decoding 'content' errored with: Cannot find a public constructor for 'Animal'.
A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
org.bson.codecs.configuration.CodecConfigurationException: An exception occurred when decoding using the AutomaticPojoCodec.
Decoding into a 'MyPet' failed with the following exception:
Failed to decode 'MyPet'. Decoding 'content' errored with: Cannot find a public constructor for 'Animal'.
A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
at org.bson.codecs.pojo.AutomaticPojoCodec.decode(AutomaticPojoCodec.java:40)
at com.mongodb.internal.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:60)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
at org.bson.internal.LazyCodec.decode(LazyCodec.java:48)
at org.bson.codecs.BsonDocumentCodec.readValue(BsonDocumentCodec.java:101)
at com.mongodb.internal.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:63)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
at com.mongodb.internal.connection.ReplyMessage.<init>(ReplyMessage.java:51)
at com.mongodb.internal.connection.InternalStreamConnection.getCommandResult(InternalStreamConnection.java:412)
at com.mongodb.internal.connection.InternalStreamConnection.access$900(InternalStreamConnection.java:75)
at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:397)
at com.mongodb.internal.connection.InternalStreamConnection$2$1.onResult(InternalStreamConnection.java:375)
at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:676)
at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:643)
at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:513)
at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:510)
at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:230)
at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:213)
at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
at java.base/sun.nio.ch.Invoker.invokeDirect(Invoker.java:158)
at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.implRead(UnixAsynchronousSocketChannelImpl.java:560)
at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:277)
at java.base/sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:298)
at com.mongodb.internal.connection.AsynchronousSocketChannelStream$AsynchronousSocketChannelAdapter.read(AsynchronousSocketChannelStream.java:136)
at com.mongodb.internal.connection.AsynchronousChannelStream.readAsync(AsynchronousChannelStream.java:109)
at com.mongodb.internal.connection.InternalStreamConnection.readAsync(InternalStreamConnection.java:510)
at com.mongodb.internal.connection.InternalStreamConnection.access$1000(InternalStreamConnection.java:75)
at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:633)
at com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback.onResult(InternalStreamConnection.java:618)
at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:513)
at com.mongodb.internal.connection.InternalStreamConnection$5.completed(InternalStreamConnection.java:510)
at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:230)
at com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:213)
at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.finishRead(UnixAsynchronousSocketChannelImpl.java:437)
at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.finish(UnixAsynchronousSocketChannelImpl.java:191)
at java.base/sun.nio.ch.UnixAsynchronousSocketChannelImpl.onEvent(UnixAsynchronousSocketChannelImpl.java:213)
at java.base/sun.nio.ch.KQueuePort$EventHandlerTask.run(KQueuePort.java:312)
at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Is there a way to return the correct class type with if-condition:
if (obj.type == "Dog") return Dog.class else Cat.class
like in Jackson
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "Dog"),
@JsonSubTypes.Type(value = Cat.class, name = "Cat"),
})
答案1
得分: 2
你可以使用 @BsonDiscriminator
注解来告诉 MongoDB 驱动程序哪个字段被用作鉴别键。这里有一个可用的示例:
import org.bson.codecs.pojo.annotations.BsonDiscriminator;
@BsonDiscriminator(key = Animal.DISCRIMINATOR_KEY)
public interface Animal {
String DISCRIMINATOR_KEY = "type";
String getType();
}
public class Cat implements Animal {
@Override
public String getType() {
return Cat.class.getName();
}
}
public class Dog implements Animal {
@Override
public String getType() {
return Dog.class.getName();
}
}
正如你所见,我在 type
字段的值中使用了完整的类名。不幸的是,自定义的鉴别值(与完整类名不同)在 Micronaut MongoDB Client 中目前无法工作。这是因为这个问题,目前还没有解决:https://github.com/micronaut-projects/micronaut-mongodb/issues/10
英文:
You can use @BsonDiscriminator
annotation to tell MongoDB driver which field is used as a discriminator key. Here is a working example:
import org.bson.codecs.pojo.annotations.BsonDiscriminator;
@BsonDiscriminator(key = Animal.DISCRIMINATOR_KEY)
public interface Animal {
String DISCRIMINATOR_KEY = "type";
String getType();
}
public class Cat implements Animal {
@Override
public String getType() {
return Cat.class.getName();
}
}
public class Dog implements Animal {
@Override
public String getType() {
return Dog.class.getName();
}
}
As you can see, I'm using full class name as a value for type
field. Unfortunately custom discriminator values (different then full class name) does not work yet in Micronaut MongoDB Client now. It is because of this issue, which still not solved: https://github.com/micronaut-projects/micronaut-mongodb/issues/10
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论