[JAVA-3665] Polymorphic POJO mapping does not work with package registration only Created: 19/Mar/20  Updated: 27/Oct/23  Resolved: 20/Apr/20

Status: Closed
Project: Java Driver
Component/s: POJO
Affects Version/s: 3.12.2
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: John Lilley Assignee: Ross Lawley
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Java 8, Mongodb server 3.6, mongodb-driver-sync 3.12.2


Attachments: Zip Archive scratch.zip    
Issue Links:
Related
related to JAVA-3707 Support package scanning for Discrimi... Backlog

 Description   

When polymorphic classes are used with @BsonDiscriminator, attempts to read/deserialize POJOs that return polymorhpic results (any of the derived classes may be returned) will fail, unless you do one of two things:

  • Register every class explicitly (not just the package)
  • Use a concrete instead of every derived class before attempting to read it.

The suggestion initially given by mongodb support (reading from Collection<DerivedClass> instead of Collection<BaseClass> is not workable, because you cannot read a list of mixed types e.g.
MongoCursor<MessageBase> cursor = msgs.find().iterator();
while (cursor.hasNext()) { ... }
{{}}

The issue seems to be that simply registering the package as prescribed
CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(myPackage).build();
CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), CodecRegistries.fromProviders(pojoCodecProvider));
mongoClient = MongoClients.create(MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(mongodbURI))
.codecRegistry(pojoCodecRegistry).build());
collection = mongoClient.getDatabase(mongodbDatabase).getCollection(mongodbCollection, entityClass);
{{}}

is not enough. Attempting to read from the database results in the following error:
ERROR: An exception occurred when decoding using the AutomaticPojoCodec.
Decoding into a 'Derived1' failed with the following exception:
Failed to decode 'Derived1'. Decoding errored with: A class could not be found for the discriminator: 'derived1'.
A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type. 
Polymorphism does work if concrete classes are first utilized before an attempt is made to read polymorphically. Please see the attached "scratch.zip" example which is built using maven. It is a CLI with three commands:

  • clear: clear the table
  • write: write two objects to table, then read them back polymorphically
  • read: read objects polymorphically

When you run this, you'll see that the write command works as expected, returning a polymorphic list, but the read command fails if that is the first command run in a process. The only difference is that the write command first inserts  Derived1 and Dervied2 before reading them back. Clearly, it must somehow convince the POJO codec to believe that the derived classes all exist before it can read them. 

Changing the codec registration to 

CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(Base.class, Derived1.class, Derived2.class).build(); CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(Base.class, Derived1.class, Derived2.class).build();

Works as expected.



 Comments   
Comment by Ross Lawley [ 21/Apr/20 ]

Hi john.lilley@redpointglobal.com,

I've added JAVA-3707 to look at scanning the class path for BsonDiscriminators.

All the best,

Ross

Comment by John Lilley [ 20/Apr/20 ]

Even if this works "as designed", I have described a valid and useful case which IMHO should be supported.  Can you reclassify this as a Story and re-open it?  A more appropriate title would be: "Support polymorphic POJO mapping with package registration and custom BsonDiscriminator".  In my workplace, we would never allow the default classname discriminator, as doing so prohibits refactoring and makes the schema fragile.

Thanks

john

Comment by Ross Lawley [ 20/Apr/20 ]

Hi john.lilley@redpointglobal.com,

Thanks for the ticket, I was wondering why I couldn't reproduce the issue and thought the library already did package based lookups for classes. Having looked at your code I noticed that the discriminators are all lowercase. If the discriminator is the name of the class, then the registered package based lookups will work. However, if it is different to the class name, then it would have to be explicitly declarated.

All the best,

Ross

Generated at Thu Feb 08 09:00:09 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.