fakemongo / fongo

faked out in-memory mongo for java
Apache License 2.0
523 stars 155 forks source link

Marshalling error when using JodaModule with Jongo #366

Open JamesTPF opened 5 years ago

JamesTPF commented 5 years ago

I have an issue where I get a marshalling error when using the JodaModule to serialise/deserialise LocalDateTime.

The problem appears to be inside FongoDBCollection. The JodaModule serialises a year/month/day as an list of [year,month,day]. The replaceListAndMap method in FongoDBCollection replaces the list with a BasicDBObject with a 0, 1 and 2 property. However, when retrieving the object from the collection there doesn't appear to be a routine to map the BasicDBObject back to a list. This means that the jackson deserialisation fails.

I have a test that recreates the issue in this repository: https://github.com/JamesTPF/fongo-collection-issue

The repository code works fine when running against a real mongo instance.

Build versions

compile 'org.jongo:jongo:1.3.0'
compile 'org.mongodb:mongo-java-driver:2.13.3'
compile 'de.undercouch:bson4jackson:2.7.0'
compile 'com.fasterxml.jackson.datatype:jackson-datatype-joda:2.9.4'
testCompile 'com.github.fakemongo:fongo:1.6.8'

Exception

org.jongo.marshall.MarshallingException: Unable to unmarshall result to class persistance.nosql.ExampleTest$TestDocument from content { "_id" : "anyId" , "testLocalDate" : { "0" : 2018 , "1" : 12 , "2" : 5}}

    at org.jongo.marshall.jackson.JacksonEngine.unmarshall(JacksonEngine.java:50)
    at org.jongo.ResultHandlerFactory$UnmarshallingResultHandler.map(ResultHandlerFactory.java:43)
    at org.jongo.FindOne.map(FindOne.java:51)
    at org.jongo.FindOne.as(FindOne.java:46)
    at persistance.nosql.ExampleTest.storeAndRetrieveObject(ExampleTest.java:30)
    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.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    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: com.fasterxml.jackson.databind.exc.MismatchedInputException: expected String, Number or JSON Array
 at [Source: de.undercouch.bson4jackson.io.LittleEndianInputStream@7cc0cdad; pos: 30] (through reference chain: persistance.nosql.ExampleTest$TestDocument["testLocalDate"])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1138)
    at com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:66)
    at com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:16)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:136)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1611)
    at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1250)
    at org.jongo.marshall.jackson.JacksonEngine.unmarshall(JacksonEngine.java:47)
    ... 27 more

Example test

package persistence.nosql;

import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.github.fakemongo.Fongo;
import com.mongodb.WriteResult;
import org.joda.time.LocalDate;
import org.jongo.Jongo;
import org.jongo.MongoCollection;
import org.jongo.marshall.jackson.JacksonMapper;
import org.jongo.marshall.jackson.oid.MongoId;
import org.junit.Before;
import org.junit.Test;

public class ExampleTest {
    private Fongo fongo = new Fongo("someDatabase");
    private Jongo jongo;

    @Before
    public void createJongo() {
        jongo = new Jongo(fongo.getDB("exampleDatabase"), new JacksonMapper.Builder()
                .registerModule(new JodaModule())
                .build());
    }

    @Test
    public void storeAndRetrieveObject() {
        final MongoCollection myCollection = jongo.getCollection("myCollection");
        final TestDocument document = new TestDocument("anyId");
        myCollection.update("{'_id': #}", document.getId()).upsert().with(document);
        myCollection.findOne("{'_id': #}", document.getId()).as(TestDocument.class);
    }

    private static class TestDocument {

        @MongoId
        private String id;
        private LocalDate testLocalDate;

        TestDocument() {
        }

        public TestDocument(final String id) {
            this.id = id;
            this.testLocalDate = new LocalDate();
        }

        public String getId() {
            return id;
        }

        public void setId(final String id) {
            this.id = id;
        }

        public LocalDate getTestLocalDate() {
            return testLocalDate;
        }

        public void setTestLocalDate(LocalDate testLocalDate) {
            this.testLocalDate = testLocalDate;
        }
    }
}