FasterXML/jackson-module-kotlin: teaching Jackson about constructors, nullability and reified types
Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.
At a glance
- What is it?
- jackson-module-kotlin is the Apache-2.0 licensed module that lets Jackson deserialize Kotlin classes without a default constructor, infer property names from constructor parameters using Kotlin runtime type information, and, since 2.19.0, refuse to hand a null back to a non-null type. It ships for Jackson 3.x and the 2.x line in parallel, with separate branches and separate Maven coordinates.
- Who is it for?
- Adopt jackson-module-kotlin if you are serialising Kotlin data classes with Jackson, because the constructor inference and the null-safety checks remove a class of runtime failures that plain Jackson leaves open, and the reified readValue extension removes the Class and TypeReference ceremony from most call sites.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.
Star history
- FasterXML/jackson-module-kotlin1,186 ★ · +0
| Repository | 2026-10-23 | 2026-11-06 |
|---|---|---|
| FasterXML/jackson-module-kotlin | 1.2k | 1.2k |
0 Stars in the last 14 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The default constructor problem, and what the module changes
Jackson's databind layer can deserialize Java beans without much help, but Kotlin's data classes are not Java beans. A Kotlin class with constructor parameters has no no-argument constructor for Jackson to use, so the rule the module describes is that previously a default constructor must have existed on the Kotlin object for Jackson to deserialize into the object.
With the module registered, that requirement goes away for the common case. Single constructor classes can be used automatically, and classes with secondary constructors or static factories are also supported. That is the whole point of the module, and it is why it exists rather than being a convenience wrapper.
Property names are inferred from the constructor parameters, using Kotlin runtime type information rather than reflection over the JVM's erased view of the class. That distinction matters: Kotlin's own metadata carries the parameter names, the nullability of each parameter and the default values, and a plain JVM reflection reader cannot see them, while a reader that only knows the Java view would lose exactly the information a Kotlin consumer needs.
The practical consequence is that a data class like `data class MyStateObject(val name: String, val age: Int)` round trips through JSON without annotations, without a no-argument constructor, and without a naming strategy that has to be told the property names.
Registering the module, three ways
The module is registered against an `ObjectMapper` instance, and there are three documented ways to do it, all shown for Jackson 3.0 and later, where the package names live under `tools.jackson`.
// With Jackson 3.0 and later
import tools.jackson.module.kotlin.jacksonObjectMapper
...
val mapper = jacksonObjectMapper()
// or
import tools.jackson.module.kotlin.registerKotlinModule
...
val mapper = ObjectMapper().registerKotlinModule()
// or
import tools.jackson.module.kotlin.jsonMapper
import tools.jackson.module.kotlin.kotlinModule
...
val mapper = jsonMapper {
addModule(kotlinModule())
}The first is a builder that produces a mapper with the module already in place, the second registers onto an existing mapper, and the third is the Jackson 3 style builder where the module is added explicitly.
The third form matters if you configure the mapper yourself, because in Jackson 3 the mapper is built through a builder rather than configured after construction. If you have a large application with a central mapper configuration, the `jsonMapper { addModule(kotlinModule()) }` shape is the one that fits it.
There is a note about configuration as well. In 2.17 and later, the `jacksonObjectMapper {}` and `registerKotlinModule {}` lambdas allow configuration for `KotlinModule`, and the README points at its configuration section for the list of available items. That is where the behaviour that does not follow from Kotlin's own defaults is set, and it is the first place to look if the module does something you did not expect.
readValue with reified generics, and the JsonNode operators
The module also provides extension functions that remove the type token ceremony from deserialization calls:
import tools.jackson.module.kotlin.jacksonObjectMapper
import tools.jackson.module.kotlin.readValue
data class MyStateObject(val name: String, val age: Int)
...
val mapper = jacksonObjectMapper()
val state = mapper.readValue<MyStateObject>(json)
// or
val state: MyStateObject = mapper.readValue(json)
// or
myMemberWithType = mapper.readValue(json)The reason this is possible is that the inferred types carry full generic information. The extension functions are reified, so using `readValue()` without the `Class` parameter reifies the type and automatically creates a `TypeReference` for Jackson. That is what lets the second and third forms above work, where the expected type comes from the assignment target or the member type rather than from a type argument.
The README also lists a table of shorthands for the deserialization methods of `ObjectMapper` and `ObjectReader`, which are the extension forms of `readValue`, `readValues`, `treeToValue` and `convertValue`, with the `ObjectReader` variants named `readValueTyped`, `readValuesTyped` and `treeToValue`. `ObjectMapper.readValue` accepts the same sources as the original, namely `JsonParser`, `File`, `String`, `Reader`, `InputStream` and `ByteArray`.
Separately, there are operator overloading extensions for the `JsonNode` inheritors, which is the ergonomic part of the module that people tend to remember:
objectNode.put("foo1", "bar").put("foo2", "baz").put("foo3", "bax")
objectNode -= "foo1"
objectNode -= listOf("foo2")
val arrayNode: ArrayNode = JsonNodeFactory.instance.arrayNode()
arrayNode += "foo"
arrayNode += true
arrayNode += 1So removing a field reads as `objectNode -= "foo1"` and appending to an array reads as `arrayNode += "foo"`, rather than as a call to a remove or add method.
Since 2.19.0, a null for a non-null type throws
The behaviour change in this module since 2.19.0 is the one to understand before you upgrade, because it turns a silent null into an exception.
The shorthands are not merely shorthands, the README says. Since 2.19.0 most of them check the deserialized value to preserve Kotlin null safety. If a null is deserialized while the reified type is non-null, a `DatabindException` is thrown instead of the null being returned.
// Throws DatabindException, because String is non-null but null was deserialized
mapper.readValue<String>("null")
// Returns null, because the reified type is nullable
mapper.readValue<String?>("null")The same check also detects values whose type is unrelated to the reified type, which the documentation says indicates that the `ObjectMapper` is incorrectly customized. That second catch is useful, because a mapper that produces the wrong shape is usually a misconfiguration rather than bad input, and failing loudly is better than returning something that will fail later.
Two details about where the exception surfaces. For `readValues` and `readValuesTyped` the check is applied to each value by the returned iterator, so the exception is thrown from `next()` or `nextValue()` rather than from the function itself, which means a stream of values can deliver some before failing. And `ObjectReader.treeToValue` is the only function that does not perform the check, declaring a nullable return type instead.
If your code currently reads a nullable JSON field into a non-null Kotlin property and relies on getting null back, that code needs to change when you take 2.19.0 or later.
Which Kotlin version pairs with which Jackson line
The compatibility matrix is short and worth reading before you pick a version, because the module is versioned to match the Jackson line it supports rather than the Kotlin one.
The stated pairs are: Jackson 3.3.x with Kotlin core 2.2 to 2.4, Jackson 3.2.x with Kotlin core 2.1 to 2.3, Jackson 3.1.x with Kotlin core 2.1 to 2.3, and Jackson 2.21.x with Kotlin core 2.1 to 2.3, where starting with version 2.21.2 compatibility with Kotlin 1.9 is provided.
The project labels the list incomplete and asks for corrections and additions through pull requests, and it notes that the versions supported by 2.17 are tentative and may change depending on the release date. The general rule it gives is that different `kotlin-core` versions are supported by different Jackson Kotlin module minor versions, which is a way of saying that upgrading Kotlin may require upgrading the module, and that the module's version is not a free choice.
On Android, the supported SDK versions are determined by `jackson-databind` rather than by this module, and the README points at the Android section of the databind repository for those details. That is a sensible division, since it is the databind layer that actually talks to the platform.
The current release lines are three, with a release for Jackson 3.2.x at 3.2.0, one for Jackson 3.1.x at 3.1.0 and one for Jackson 2.21.x at 2.21.1, each with its own build badge and branch. The default branch in the repository is `3.x`, so the 2.x line lives on its own branches, which is how the project maintains both major lines in parallel.
Dependencies, releases and where to look for help
The module is published to Maven Central, and the coordinates changed with the Jackson 3 move. For Jackson 3 the group is `tools.jackson.module` and the artifact is `jackson-module-kotlin`:
<dependency>
<groupId>tools.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>3.2.0</version>
</dependency>In Gradle the same dependency is written as `implementation "tools.jackson.module:jackson-module-kotlin:3.2.+"`, using a dynamic minor version. For the 2.x line the coordinates are the older `com.fasterxml.jackson.module` group, and the Tidelift badge in the README points at that artifact, which is a good way to tell the two lines apart in an existing build file.
One prerequisite is easy to miss. Releases require that you have included the Kotlin stdlib and reflect libraries already. The reflect library in particular is not optional, because it is what lets the module read Kotlin's own metadata, and a missing dependency shows up as a failure at deserialization time rather than as a missing class at startup.
The repository has no GitHub releases, so the version history lives in the `release-notes/` directory, which the change log badge links to, and the build is Maven based with a `pom.xml` and the `mvnw` wrapper, alongside CircleCI and Travis configuration files from earlier eras.
The licence is Apache-2.0, and the tree includes a `SECURITY.md` file, which is the file to read if you find a vulnerability in a module that processes untrusted JSON in a large number of services. The Kotlin Slack badge in the README points at the Kotlin community channel, which is where questions about the interaction between the module and Kotlin's compiler or runtime are best asked.
Editorial conclusion
Adopt jackson-module-kotlin if you are serialising Kotlin data classes with Jackson, because the constructor inference and the null-safety checks remove a class of runtime failures that plain Jackson leaves open, and the reified readValue extension removes the Class and TypeReference ceremony from most call sites. Do not adopt it if you are on Jackson 2.x and not planning to move, since the module now ships in two parallel lines with different Maven coordinates and different Kotlin version ranges, which means a dependency mistake is easy to make. Verify first which Jackson line your project is on, that your Kotlin compiler version falls inside the supported range for that Jackson minor version, that the Kotlin stdlib and reflect libraries are already on your classpath, and that any code relying on a null being returned for a non-null property is updated, because since 2.19.0 that path throws instead.
Frequently asked questions
Why do I need jackson-module-kotlin for my data classes?
Without it, Jackson needs a default constructor on the Kotlin object to deserialize into it. With the module registered, single constructor classes work automatically, and classes with secondary constructors or static factories are supported too, and property names are inferred from the constructor parameters using Kotlin runtime type information.
How do I register the Kotlin module with an ObjectMapper?
For Jackson 3.0 and later you can use `jacksonObjectMapper()`, call `registerKotlinModule()` on an existing mapper, or use the builder form `jsonMapper { addModule(kotlinModule()) }`. Since 2.17 the `jacksonObjectMapper {}` and `registerKotlinModule {}` lambdas also allow configuration for the Kotlin module itself.
What happens when readValue deserializes null into a non-null Kotlin type?
Since 2.19.0 the shorthands check the deserialized value to preserve Kotlin null safety, so a null deserialized while the reified type is non-null throws a `DatabindException` instead of returning null. `readValue<String?>` still returns null, and the same check detects values whose type is unrelated to the reified type.
Which Kotlin version works with my Jackson version?
The project lists Jackson 3.3.x with Kotlin core 2.2 to 2.4, Jackson 3.2.x and 3.1.x with Kotlin core 2.1 to 2.3, and Jackson 2.21.x with Kotlin core 2.1 to 2.3, with Kotlin 1.9 compatibility from 2.21.2. The list is marked incomplete and the project asks for corrections through pull requests.
What extra dependencies does jackson-module-kotlin need?
The Kotlin stdlib and reflect libraries must already be on your classpath, since the module reads Kotlin's own runtime metadata to infer property names. The Maven coordinates for the Jackson 3 line are the `tools.jackson.module` group with the `jackson-module-kotlin` artifact.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fasterxml-jackson-module-kotlin)Star history chart for your README
[](https://hysenlabs.com/projects/fasterxml-jackson-module-kotlin)Was this useful?