ModuleMaven artifactAdds support for
JavaTimeModulejackson-datatype-jsr310java.time.* — LocalDate, Instant, Duration, ZonedDateTime…
ParameterNamesModulejackson-module-parameter-namesConstructor/method parameter names without @JsonProperty
Jdk8Modulejackson-datatype-jdk8Optional<T>, OptionalInt, OptionalLong
KotlinModulejackson-module-kotlinKotlin data classes, default parameters, null safety
GuavaModulejackson-datatype-guavaGuava ImmutableList, Multimap, Optional
BlackbirdModulejackson-module-blackbirdBytecode-generated accessors — faster than reflection
SimpleModuleBuilt-inYour own serializers, deserializers, key (de)serializers
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.17.2</version> </dependency> import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.databind.SerializationFeature; import java.time.*; ObjectMapper mapper = new ObjectMapper() .registerModule(new JavaTimeModule()) // Write dates as ISO strings, not epoch milliseconds .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); record Event(String name, LocalDate date, Instant createdAt, Duration duration) {} Event event = new Event( "Conference", LocalDate.of(2025, 6, 15), Instant.parse("2025-01-10T09:00:00Z"), Duration.ofHours(8)); String json = mapper.writeValueAsString(event); // {"name":"Conference","date":"2025-06-15","createdAt":"2025-01-10T09:00:00Z","duration":28800.000000000} Event restored = mapper.readValue(json, Event.class); System.out.println(restored.date()); // 2025-06-15 Without disable(WRITE_DATES_AS_TIMESTAMPS), Jackson serialises LocalDate as an array [2025,6,15] and Instant as a number. Always disable this feature for human-readable ISO-8601 output.

Java erases generic type parameters at runtime, so mapper.readValue(json, List.class) produces a raw List<LinkedHashMap>, not List<User>. Use TypeReference to preserve the generic information.

import com.fasterxml.jackson.core.type.TypeReference; import java.util.*; ObjectMapper mapper = new ObjectMapper(); // WRONG — produces List<LinkedHashMap>, not List<User> List rawList = mapper.readValue(json, List.class); // CORRECT — TypeReference captures the full generic type at compile time List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {}); // Map Map<String, User> userMap = mapper.readValue(json, new TypeReference<Map<String, User>>() {}); // Nested generics List<Map<String, List<Integer>>> complex = mapper.readValue(json, new TypeReference<List<Map<String, List<Integer>>>>() {}); // With JavaType for dynamic construction JavaType userListType = mapper.getTypeFactory() .constructCollectionType(List.class, User.class); List<User> users2 = mapper.readValue(json, userListType); // Parameterized with two type params JavaType mapType = mapper.getTypeFactory() .constructMapType(HashMap.class, String.class, User.class);
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jdk8</artifactId> <version>2.17.2</version> </dependency> import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import java.util.Optional; ObjectMapper mapper = new ObjectMapper() .registerModule(new Jdk8Module()); record Profile(String name, Optional<String> bio) {} // With value String json1 = mapper.writeValueAsString(new Profile("Alice", Optional.of("Developer"))); // {"name":"Alice","bio":"Developer"} // Without value String json2 = mapper.writeValueAsString(new Profile("Bob", Optional.empty())); // {"name":"Bob","bio":null} Profile p = mapper.readValue("{\"name\":\"Carol\",\"bio\":\"Writer\"}", Profile.class); p.bio().ifPresent(System.out::println); // Writer

With ParameterNamesModule and the -parameters compiler flag, Jackson can match JSON fields to constructor parameters by name without requiring @JsonProperty on every parameter.

<!-- Maven: enable -parameters compiler flag --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <compilerArgs><arg>-parameters</arg></compilerArgs> </configuration> </plugin> import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import com.fasterxml.jackson.annotation.JsonCreator; ObjectMapper mapper = new ObjectMapper() .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES)); // No @JsonProperty needed — parameter names resolved at runtime public record User(int id, String name, String email) {} User user = mapper.readValue("{\"id\":1,\"name\":\"Alice\",\"email\":\"a@b.com\"}", User.class); System.out.println(user.name()); // Alice Spring Boot auto-configures ParameterNamesModule, JavaTimeModule, and Jdk8Module automatically when the jars are on the classpath. You only need to register them manually in plain Java applications.
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Currency; // Custom serializer for java.util.Currency class CurrencySerializer extends com.fasterxml.jackson.databind.ser.std.StdSerializer<Currency> { CurrencySerializer() { super(Currency.class); } @Override public void serialize(Currency c, com.fasterxml.jackson.core.JsonGenerator gen, com.fasterxml.jackson.databind.SerializerProvider p) throws java.io.IOException { gen.writeString(c.getCurrencyCode()); } } // Custom deserializer for java.util.Currency class CurrencyDeserializer extends com.fasterxml.jackson.databind.deser.std.StdDeserializer<Currency> { CurrencyDeserializer() { super(Currency.class); } @Override public Currency deserialize(com.fasterxml.jackson.core.JsonParser p, com.fasterxml.jackson.databind.DeserializationContext ctx) throws java.io.IOException { return Currency.getInstance(p.getText()); } } // Assemble into a named module SimpleModule currencyModule = new SimpleModule("CurrencyModule") .addSerializer(Currency.class, new CurrencySerializer()) .addDeserializer(Currency.class, new CurrencyDeserializer()); ObjectMapper mapper = new ObjectMapper().registerModule(currencyModule); record Payment(String id, Currency currency, double amount) {} String json = mapper.writeValueAsString(new Payment("P1", Currency.getInstance("EUR"), 99.99)); // {"id":"P1","currency":"EUR","amount":99.99} Payment p = mapper.readValue(json, Payment.class); System.out.println(p.currency().getCurrencyCode()); // EUR