Search code examples
mongodbjacksonkotlinjongojackson-modules

In Kotlin, why Jackson fails unmarshalling non-annotated object in some cases and not in others


I'm using Kotlin with Jongo to access MongoDB. Jongo uses Jackson to serialize/deserialize objects in order to save and read them from MongoDB. I use Jackson-Kotlin module to help serialize Kotlin data classes using constructors.

Here's an example of a dataclass that serializes just fine:

data class Workflow (
    @field:[MongoId MongoObjectId] @param:MongoId
    var id: String? = null,

    val name: String,
    val states: Map<String, State>
)

Here's an example of a similar class that fails to deserialize:

data class Session (
    @field:[MongoObjectId MongoId] @param:MongoId
    var id: String? = null,

    var status: CallStatus,
    var currentState: String,
    var context: MutableMap<String, Any?>,
    val events: MutableMap<String, Event>
)

Following exception is thrown by Jongo because Jackson deserialization fails:

org.jongo.marshall.MarshallingException: Unable to unmarshall result to class example.model.Session from content { "_id" : { "$oid" : "56c4976aceb2503bf3cd92c2"} , "status" : "Ongoing" , "currentState" : "Start" , "context" : { } , "events" : { }}
... bunch of stack trace entries ...
Caused by: java.lang.IllegalArgumentException: Argument #1 of constructor [constructor for example.model.Session, annotations: [null]] has no property name annotation; must have name when multiple-parameter constructor annotated as Creator

It does work if I fully annotate the Session data class like this:

data class Session (
    @field:[MongoObjectId MongoId] @param:MongoId
    var id: String? = null,

    @JsonProperty("status")
    var status: CallStatus,

    @JsonProperty("currentState")
    var currentState: String,

    @JsonProperty("context")
    var context: MutableMap<String, Any?>,

    @JsonProperty("events")
    val events: MutableMap<String, Event>
}

My question is, why does it work for Workflow? What's the subtle difference that makes unmarshalling fail for Session data class, when it's not fully annotated?

EDIT

Difference was that I tested Workflow test case running it from Gradle, which used different version of Kotlin then the Session test case that I run from IDEA IDE. Update of IDEA's Kotlin plugin also updated Kotlin version that IDEA uses to run test cases, which I didn't notice. This resulted in mismatched versions of Kotlin and Jackson-Kotlin library. Accepted answer below pointed out what needs to be configured in order get things working again.


Solution

  • You would get this error if MongoDB is using an ObjectMapper that does not have the Jackson-Kotlin module registered into it. Your JsonProperty annotations are basically doing the same as what the module does implicitly. But if it is not present, you would receive about the same error message.

    Things to look at:

    • You have compatible version of Jackson-Kotlin module that matches your version of the Kotlin code. For RC 1050 or newer, you need the latest releases of Jackson module mentioned in the README.MD file on GitHub.

      Older versions of the Jackson-Kotlin module are not compatible with Kotlin 1.0.0. You must update or you will have silent failures (the module cannot recognize a Kotlin class, so ignores it). Releases for Kotlin 1.0.0 will be available on Maven Central shortly. In the meantime use the EAP repository:

      maven {
         url  "http://dl.bintray.com/jaysonminard/kohesive"
      }
      

      For Kotlin 1.0.0, use one of:

      • release 2.7.1-1 (for Jackson 2.7.x)
      • release 2.6.5-2 (for Jackson 2.6.x)
      • release 2.5.5-2 (for Jackson 2.5.x)

      Later this will move back to Maven Central.

    • what is the actual JSON being sent to Jackson

    • is this error from MongoDB in the stacktrace, or is Jackson involved for sure (did it preprocess and determine an error before calling Jackson)?
    • how is MongoDB getting its ObjectMapper and how did you try to configure that?