[JAVA-2587] Null property during serialization breaks deserialization with @BsonCreator Created: 24/Aug/17  Updated: 29/Oct/23  Resolved: 08/Sep/17

Status: Closed
Project: Java Driver
Component/s: POJO
Affects Version/s: 3.5.0
Fix Version/s: 3.6.0

Type: Bug Priority: Major - P3
Reporter: Paulo Melo Assignee: Ross Lawley
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to JAVA-4927 Null property during serialization br... Backlog

 Description   

If a POJO is saved with a field with a null value, it ends up not being serialized in the resulting Json, causing the deserialization to fail with a missing field exception:

Could not construct new instance of: Candidate. Missing the following properties: [mothersName]

In this case, my POJO has the property `mothersName` as `null`.



 Comments   
Comment by Ross Lawley [ 06/Sep/17 ]

Scheduled to be backported to 3.5.1

Comment by Paulo Melo [ 06/Sep/17 ]

Awesome, thanks!

Comment by Ross Lawley [ 06/Sep/17 ]

Thanks paulo_melo for the ticket,

This is now fixed and scheduled for the 3.6.0 release. We now try and default any missing parameters to null. This lets the creator handle if nulls are appropriate or not. If the creator throws the original missing parameters error will be thrown.

All the best,

Ross

Comment by Githook User [ 05/Sep/17 ]

Author:

{'username': 'rozza', 'name': 'Ross Lawley', 'email': 'ross.lawley@gmail.com'}

Message: Allow for missing parameters when creating new instances

Make any missing @BsonProperty values null, then try to construct a new instance.
This allows non serialized null values to be round tripped.

JAVA-2587
Branch: master
https://github.com/mongodb/mongo-java-driver/commit/1a9baf00870479622ea4f0b1be96ea0bb7ed6a6c

Comment by Paulo Melo [ 04/Sep/17 ]

Thanks!

In Jackson (where I suppose you guys got the inspiration) you can define if a given JsonProperty is required or not, as a second parameter to the annotation, being required = false the default value.

I believe that BsonProperty can have the same behavior, but I think that adding this parameter would break backwards compatibility..

Comment by Ross Lawley [ 04/Sep/17 ]

Hi paulo_melo,

Thanks for that, I can see the issue, as no value is stored in the database, there is no value that the @BsonCreator can use. If the null values were stored in the database, I suspect this would work as expected.

I'll investigate if the BsonCreator convention can determine if nulls are acceptable for missing values based on the underlying ClassModel.

Ross

Comment by Paulo Melo [ 04/Sep/17 ]

Hello and sorry for the delay, somehow I didn't get notified of this.

The POJO is this one:

public class Candidate {
 
    public final String _id;
    public final String name;
    public final String email;
    public final String password;
    public final String cpf;
    public final String motherName;
    public final String rg;
    public final EducationLevel educationLevel;
    public Address address;
    public Phone phone;
 
    @BsonCreator
    public Candidate(@BsonProperty("_id") String _id,
                     @BsonProperty("name") String name,
                     @BsonProperty("email") String email,
                     @BsonProperty("password") String password,
                     @BsonProperty("cpf") String cpf,
                     @BsonProperty("motherName") String motherName,
                     @BsonProperty("rg") String rg,
                     @BsonProperty("educationLevel") EducationLevel educationLevel,
                     @BsonProperty("address") Address address,
                     @BsonProperty("phone") Phone phone) {
 
        this._id = _id;
        this.name = name;
        this.email = email;
        this.password = password;
        this.motherName = motherName;
        this.cpf = cpf;
        this.rg = rg;
        this.educationLevel = educationLevel;
        this.address = address;
        this.phone = phone;
    }
}

This is how I've instantiated the class:

new Candidate("", "nome", "email@email", "123465", "1234", null, "1234", EducationLevel.FUNDAMENTAL, null, null);

See that I've nulled out three parameters: address, phone and motherName.

This is the stacktrace I get when trying to fetch my Candidate from the database:

org.bson.codecs.configuration.CodecConfigurationException: An exception occurred when decoding using the AutomaticPojoCodec.
Decoding into a 'Candidate' failed with the following exception:
 
Could not construct new instance of: Candidate. Missing the following properties: [address, phone, motherName]
 
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.connection.ReplyMessage.<init>(ReplyMessage.java:50)
	at com.mongodb.connection.QueryProtocol.execute(QueryProtocol.java:286)
	at com.mongodb.connection.QueryProtocol.execute(QueryProtocol.java:54)
	at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:159)
	at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:289)
	at com.mongodb.connection.DefaultServerConnection.query(DefaultServerConnection.java:212)
	at com.mongodb.operation.FindOperation$1.call(FindOperation.java:724)
	at com.mongodb.operation.FindOperation$1.call(FindOperation.java:709)
	at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:433)
	at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:406)
	at com.mongodb.operation.FindOperation.execute(FindOperation.java:709)
	at com.mongodb.operation.FindOperation.execute(FindOperation.java:81)
	at com.mongodb.Mongo.execute(Mongo.java:810)
	at com.mongodb.Mongo$2.execute(Mongo.java:797)
	at com.mongodb.FindIterableImpl$FindOperationIterable.first(FindIterableImpl.java:273)
	at com.mongodb.FindIterableImpl.first(FindIterableImpl.java:205)
	at br.fundepes.repository.CandidateRepository.findById(CandidateRepository.java:38)
	at repository.candidate.CreateTest.shouldCreateCandidate(CreateTest.java:35)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Could not construct new instance of: Candidate. Missing the following properties: [address, phone, motherName]
	at org.bson.codecs.pojo.InstanceCreatorImpl.getInstance(InstanceCreatorImpl.java:75)
	at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:104)
	at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:107)
	at org.bson.codecs.pojo.AutomaticPojoCodec.decode(AutomaticPojoCodec.java:37)
	... 40 more

The behavior I expected was to deserialize these parameters as null, instead of throwing an exception.

Comment by Ross Lawley [ 04/Sep/17 ]

Hi paulo_melo,

Can you provide anymore information? So we can replicate this issue and look to provide a solution.

Ross

Comment by Jeffrey Yemin [ 24/Aug/17 ]

Thanks for the report. To help ensure that we understand the exact issue you're running in to, please provide the source code for your Pojo as well as the stack trace of the exception that's being thrown.

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