This guide aims to "Connect the Dots" for Jackson 2.x to 3.x migration, helping developers by outlining the process. It is not a comprehensive guide, or check list.
Guide mostly references documentation in other repos and provides a high-level summary with appropriate links.
- New Maven group-id and Java package:
tools.jackson(2.x usedcom.fasterxml.jackson)- See JSTEP-1 for details
- Exception:
jackson-annotations: 2.x version still used with 3.x, so no group-id/Java package change- See this discussion for rationale
- Jackson 3.0 uses
jackson-annotations2.20 - "Exception to Exception": annotations within
jackson-databindlike@JsonSerializeand@JsonDeserializeDO move to new Java package (tools.jackson.databind.annotation). Same for format-specific annotation like XML (jackson-dataformat-xml) ones.
- All
@Deprecated(as of 2.20) methods, fields and classes are removed from 3.0- Javadocs in Jackson
2.20updated to indicate replacements where available (incomplete: PRs welcome for more!)
- Javadocs in Jackson
- Renaming of Core Entities (classes), methods, fields
- See JSTEP-6 for rationale, reference to notable renamings
- Javadocs in Jackson
2.20updated to indicate new names where available (incomplete: PRs welcome for more!)
- Changes to Default Configuration Settings (esp. various XxxFeatures)
- See JSTEP-2 for rationale, the set of changes made
- Immutability of
ObjectMapper,JsonFactoryObjectMapperandJsonFactory(and their sub-types) are fully immutable in 3.x: instances to be constructed using Builder pattern
- Use of format-aligned
ObjectMappermandatory:new YAMLMapper(),new XmlMapper()- Old
new ObjectMapper(new YAMLFactory())no longer allowed
- Old
- Unchecked exceptions: all Jackson exceptions now
RuntimeExceptions (unchecked)- JSTEP-4 explains rationale, changes
- Base exception (
JsonProcessingExceptionin 2.x, renamed asJacksonException) now extendsRuntimeExceptionand NOTIOException(like 2.x did)
- Embedding and Removal of "Java 8 modules"
- All 3 "Java 8 modules", that were separate in Jackson 2.x, are now built-in to
jackson-databind(no need to register separately)jackson-module-parameter-names: auto-detection of Constructor parameter namesjackson-datatype-jdk8: support forjava.util.Optionaland other optional types (OptionalDouble, ...)jackson-datatype-jsr310: supportjava.timetypes (added in 3.0.0-rc3)
- All 3 "Java 8 modules", that were separate in Jackson 2.x, are now built-in to
For the full list of all issues resolved for 3.0, see Jackson 3.0 Release Notes.
Starting from the high-level change list, we can see the need for following changes:
- Maven group id, Java package change
- Need to update build files (
pom.xml,build.gradle) to use new group id (com.fasterxml.jackson.core->tools.jackson.coreand so on) - Need to change import statements due to change in Java package (
com.fasterxml.jackson->tools.jackson-- EXCEPT not forjackson-annotations)
- Need to update build files (
@Deprecatedmethod, field, class removal:- Need to replace with non-Deprecated alternatives, as per
2.20Javadocs updated to indicate replacement where possible - See later Section for a set of common cases
- Need to replace with non-Deprecated alternatives, as per
- Renaming of Core Entities (classes), methods, fields
- Need to change references to use new name (including
importstatements):2.20Javadocs updated to indicate replacement where possible - JSTEP-6 includes a list (likely incomplete) of renamed things as well
- Need to change references to use new name (including
- Changes to Default Configuration Settings
- MAY need to override some defaults (where existing 2.x behavior preferred) -- but most changes are to settings developers prefer so unlikely to need to change all
JsonMapper.builderWithJackson2Defaults()may be used to use some of legacy configuration settings (cannot change all defaults but can help migration)
- JSTEP-2 lists all default changes
- MAY need to override some defaults (where existing 2.x behavior preferred) -- but most changes are to settings developers prefer so unlikely to need to change all
- Immutability of
ObjectMapper,JsonFactoryObjectMapper/JsonMapper: convert direct configuration with Builder alternatives:JsonMapper.builder().enable(...).build()JsonFactory/TokenStreamFactory: convert direct configuration with Builder alternatives:JsonFactory.builder().enable(...).build()
- Use of format-aligned
ObjectMappermandatory- Format-specific sub-types already exist for all formats in 2.20
- In 3.0, constructing plain
ObjectMapperwith format-specificTokenStreamFactoryno longer allowed
- Unchecked exceptions
- May require changes to handling since catching Jackson exceptions now optional
- No need to declare
throwsclause for Jackson calls - Base exceptions renamed; specifically:
JsonProcessingException->JacksonExceptionJsonMappingException->DatabindException
- Embedding and Removal of "Java 8 modules"
- Simply remove Maven/Gradle dependencies, module registrations
- Jackson-future-ideas Github repo contains majority of planning and discussion for Jackson 3 preparation. It includes:
- JSTEPs (Jackson STrategic Enhancement Proposals)
- Sources for most major changes
- Github Discussions section in the same repo as JSTEP
- see first (but not last) discussion here as example
- JSTEPs (Jackson STrategic Enhancement Proposals)
Changes to import statements should be quite mechanical:
- Replace
com.fasterxml.jackson.withtools.jackson.everywhere- EXCEPT NOT for
com.fasterxml.jackson.annotation
- EXCEPT NOT for
Similarly change to Maven Group id in the build files (pom.xml, build.gradle) should be mechanical:
- Replace
com.fasterxml.jacksonwithtools.jacksonfor dependencies- BUT NOT for
com.fasterxml.jackson.annotation
- BUT NOT for
But beyond this, if you are not yet using jackson-bom for enforcing compatible set of versions, we would highly encourage starting to do so.
This could additionally help avoid most of complexity of jackson-annotations dependency since it is possible to rely on annotations package as transitive dependency. For Maven you can just do:
<project>
<!-- ... -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>tools.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>3.0.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<!-- ... -->
<dependencies>
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- jackson-core and jackson-annotations transitive dependencies -->
</dependencies>
<project>and for Gradle you can use something like:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation platform("tools.jackson:jackson-bom:3.0.0")
// Now declare Jackson modules WITHOUT versions
implementation "com.fasterxml.jackson.core:jackson-databind"
}
It is necessary to convert @Deprecated methods/fields/classes with document alternatives where ones exist.
Jackson 2.20 Javadocs include replacement in many cases.
Here are notes on most commonly encountered cases.
None reported yet
None reported yet
com.fasterxml.jackson.databind.MappingJsonFactoryis removed -- while not Deprecated in 2.x (where it is used), implementation is neither needed nor possible to support as-is.
Jackson 1.x and 2.x contained functionality for auto-detecting format of arbitrary content to decode: functionality was part of jackson-core -- Java classes under com.fasterxml.jackson.core.format (like DataFormatDetector) -- (and implemented by jackson-dataformat-xxx components for non-JSON formats).
But due to complexity of implementation, problems with API handling, and lack of usage, this functionality was dropped from 3.0. No replacement exists
Similar to deprecations, it is necessary to change old references to use new name (including import statements): 2.20 Javadocs were updated in some cases to indicate replacement (if available).
Further JSTEP-6 includes a list of renamed things as well -- possibly incomplete list, but useful.
From that, here are ones you are most likely to encounter.
Regular classes:
JsonFactorysplit- API extracted as
TokenStreamFactory - Implementation moved under
tools.jackson.core.json(note the added "json" segment)
- API extracted as
JsonStreamContext->TokenStreamContextJsonLocation->TokenStreamLocation
Exception types:
JsonProcessingException->JacksonException(ultimate base exception)JsonParseException-> `StreamReadExceptionJsonEOFException->UnexpectedEndOfInputExceptionJsonGenerationException->StreamWriteException
Methods:
JsonGenerator:- replace references in method names to "field" with "property"
writeObject()->writePOJO()getCurrentValue()->currentValue()setCurrentValue()->assignCurrentValue()
JsonParser:- replace references in method names to "field" with "property"
- replace "xxxTextYyy" methods (like
getTextCharacters()) with "xxxStringYyy" methods (likegetStringCharacters()) getCurrentLocation()->currentLocation()getTokenLocation()->currentTokenLocation()getCurrentValue()->currentValue()setCurrentValue()->assignCurrentValue()
Fields:
JsonToken.FIELD_NAME->JsonToken.PROPERTY_NAME
Regular classes:
BeanDeserializerModifier->ValueDeserializerModifierBeanSerializerModifier->ValueSerializerModifierJsonDeserializer->ValueDeserializerJsonSerializer->ValueSerializerJsonSerializable->JacksonSerializableModule->JacksonModuleSerializerProvider->SerializationContextTextNode->StringNode
Exception types:
JsonMappingException->DatabindException
Methods:
- For
JsonNodemany renamings: see JSTEP-3 for details
JSTEP-2 lists all changes. In general these changes do not necessarily cause problems or require changes: however, if you do observe runtime problems (or new unit test failures), it is good to consider possibility some default config setting change could be the cause. But not all changes are equally likely to cause compatibility problems: here are ones that are considered most likely to cause problems or observed behavior changes:
MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS(disabled in 3.0): this non-intuitive feature may have masked actual problems with Immutable classes, wherein Jackson forcibly overwrote values offinalfields (which is possible via Reflection`!), but Developer assumed a Constructor was being used.- "Is it a Bug or Feature?" -- disabled since newer JVMs less likely to allow feature to work.
MapperFeature.DEFAULT_VIEW_INCLUSION(disabled in 3.0): simple configuration change, but significant impact for@JsonViewusageMapperFeature.SORT_PROPERTIES_ALPHABETICALLY(enabled in 3.0): likely to change default ordering of Property serialization for POJOs (where@JsonPropertyOrdernot used)- Highly visible and may break brittle unit tests (ones that assume specific ordering)
MapperFeature.USE_GETTERS_AS_SETTERS(disabled in 3.0): another highly non-intuitive feature; but one that may have masked actual problems (no Setter or Constructor for passingCollection/Mapvalued properties)- Originally included for JAXB compatibility
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES(disabled in 3.0): May mask real issues with name mismatchDeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES(enabled in 3.0): May start failing@JsonCreatorusage where missing values for primitive (likeint) valued properties can start failing.
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS(enabled in 3.0): Highly visible change to serialization; may break unit tests
Since both ObjectMapper and JsonFactory (TokenStreamFactory) -- along with their subtypes -- are fully immutable in 3.0, neither has direct configurability: no simple setters, or methods to configure handlers.
Instead, Builder-based configuration is needed:
A simple example of constructing JSON-handling ObjectMapper:
final JsonMapper mapper = JsonMapper.builder() // format-specific builders
.addModule(new JodaModule()) // to use Joda date/time types
.enable(JsonWriteFeature.ESCAPE_NON_ASCII) // configure JSON-escaping
.build();
Note, too, that given a mapper instance, you CAN create a Builder with its settings to create a re-configured instance:
JsonMapper mapper2 = mapper.rebuild()
.enable(SerializationFeature.INDENT_OUTPUT)
.build();
Although use of
new ObjectMapper()
is still allowed, recommended to use one of
new JsonMapper()
JsonMapper.builder().builder()
recommend. And all construction of generic ObjectMapper:
new ObjectMapper(new YAMLFactory()); // and similar
MUST be converted to format-specific `ObjectMapper`` subtypes:
new YAMLMapper(new YAMLFactory());
new YAMLMapper(); // same as above
new YAMLMapper(YAMLFactory().builder()
// configure
.build());
In addition, it may make sense to start passing typed mapper instances along: JsonMapper instead of ObjectMapper (unless format-agnostic handling needs to be supported).
No additional suggestions.
No additional suggestions.