JSTEP 2 - FasterXML/jackson-future-ideas GitHub Wiki
(Back to JSTEP page)
Jackson 3 default settings, behavior changes
Author
Tatu Saloranta (@cowtowncoder), Joo Hyuk Kim (@JooHyukKim)
Version history
- 2026-08-09: Add rationale for default changes from future-ideas#107, document
MapperFeature.DETECT_PARAMETER_NAMES, correctStreamWriteFeature.USE_FAST_DOUBLE_WRITERstatus - @JooHyukKim - 2025-10-11: Add
TokenStreamFactory.Feature.INTERN_PROPERTY_NAMES - 2025-08-27: Some notes on Date/Time handling changes.
- 2025-07-24: Update wrt revert of default change for
JsonWriteFeature.ESCAPE_FORWARD_SLASHESdefault - 2025-05-12: Update wrt CBOR read/write feature defaults - @cowtowncoder
- 2025-05-05: Update wrt
DeserializationFeature.FAIL_ON_UNEXPECTED_VIEW_PROPERTIESrevert - @cowtowncoder - 2025-05-03: Update wrt
MonthSerializerandMonthDeserializerbehavior change - @JooHyukKim - 2025-04-08: Added
DateTimeFeature.ONE_BASED_MONTHS- @cowtowncoder - 2025-04-06: Added
MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS- @cowtowncoder - 2025-02-05: Updates wrt
XmlWriteFeaturedefault changes - @cowtowncoder - 2025-02-01: Update wrt
SerializationFeature.WRITE_DATES_AS_TIMESTAMPSdefault change - @cowtowncoder - 2025-01-31: Update wrt
SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPSdefault change - @cowtowncoder - 2025-01-29: Update wrt
DeserializationFeature.FAIL_ON_TRAILING_TOKENSdefault change - @cowtowncoder - 2025-01-19: Add Kotlin module proposed change links - @cowtowncoder
- 2024-07-21:
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIESdefault changed - @cowtowncoder - 2024-06-06:
DeserializationFeature.READ_ENUMS_USING_TO_STRING,SerializationFeature.WRITE_ENUMS_USING_TO_STRINGdefaults changed - @cowtowncoder - 2024-05-31:
MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORSdefault changed - @cowtowncoder - 2019-02-10: create first version from earlier notes - @cowtowncoder
Status
Complete as of 3.0.0 release.
Overview
During Jackson 2.x development, certain default settings have proven either problematic by development team, or non-intuitive/non-optimal by users: latter indicated both by filed issues and comments on mailing list discussions. Since default settings and behavior are important part of the public API, changes have had to wait until major version change in most cases.
This document details proposed/planned as well as implemented changes.
Changes to jackson-core defaults
JsonReadFeature
No changes
JsonWriteFeature
No changes
- NOTE: initially planned change to
JsonWriteFeature.ESCAPE_FORWARD_SLASHESdefaults were rolled back in3.0.0-rc7
StreamReadConstraints
DEFAULT_MAX_DEPTHto 500 (from 1,000) (see core#1233)- Rationale: the old limit was high enough that deeply nested fuzz/adversarial input could still hit a JVM
StackOverflowErrorbefore Jackson's guard triggered. 500 remains well above expected real-world document depth, while staying below the observed stack-overflow zone.
- Rationale: the old limit was high enough that deeply nested fuzz/adversarial input could still hit a JVM
StreamWriteConstraints
DEFAULT_MAX_DEPTHto 500 (from 1,000) (see core#1233)- Rationale: same as
StreamReadConstraints.DEFAULT_MAX_DEPTH; the limit should protect Jackson before native stack depth becomes the failure mode, without affecting legitimate content.
- Rationale: same as
StreamReadFeature
USE_FAST_DOUBLE_PARSER: enabled by default in 3.0 (see core#1231)USE_FAST_BIG_NUMBER_PARSER: enabled by default in 3.0 (see core#1231)- Rationale: the faster number parsers were introduced as opt-in in Jackson 2.14 to reduce compatibility risk. After a long opt-in period without correctness reports, enabling them by default in the major-version boundary was considered reasonable for better out-of-the-box number parsing performance.
StreamWriteFeature
USE_FAST_DOUBLE_WRITER: default unchanged -- remains disabled in 3.0 (see core#1231, source)- Rationale: a writer-side default flip was considered, but rejected because newer JDKs already provide equivalent or better double-to-string handling. The feature remains opt-in, primarily useful for older supported JDKs.
TokenStreamFactory.Feature
INTERN_PROPERTY_NAMES: disabled by default in 3.0 (see core#378)- Rationale: interning property names used to speed databind property matching through identity checks, but it had memory-management costs for high-cardinality names and no benefit for streaming-only usage. Jackson 3's
FieldNameMatcher/nextFieldName(...)path removes the old need for interning while preserving or improving databind performance.
- Rationale: interning property names used to speed databind property matching through identity checks, but it had memory-management costs for high-cardinality names and no benefit for streaming-only usage. Jackson 3's
Changes to jackson-databind defaults
MapperFeature
First, following deprecated features have been removed from 3.0:
USE_STD_BEAN_NAMING: should always be enabled, no need for old slightly differing algorithmAUTO_DETECT_xxx: old limited settings superseded by more granular settings -- frees up 5 featuresAUTO_DETECT_CREATORSAUTO_DETECT_FIELDSAUTO_DETECT_GETTERSAUTO_DETECT_IS_GETTERSAUTO_DETECT_SETTERS
And defaults to following features have been changed:
ALLOW_FINAL_FIELDS_AS_MUTATORS: default tofalse-- while enabling may be useful sometimes, it confuses users in general and is unlikely to even work with future JDKs/JVMsDETECT_PARAMETER_NAMES: default totrue- Rationale: Jackson 3 integrates the Jackson 2.x
jackson-parameter-namesmodule functionality into databind and enables it by default. This lets constructor and factory method parameter names be used as implicit names, avoiding extra@JsonPropertyannotations when classes are compiled with-parameters. - Compatibility note: this may affect creator auto-detection and creator-property ordering compared with a Jackson 2.x mapper that did not register
jackson-parameter-names.MapperBuilder.configureForJackson2()disables this feature for Jackson 2.x-compatible defaults. - databind#5314
- Rationale: Jackson 3 integrates the Jackson 2.x
DEFAULT_VIEW_INCLUSION: default tofalse: has been requested by users and change initially approved- Rationale: the 2.x default made unannotated properties included even when a view was active, which made
@JsonViewcloser to opt-out behavior.falsemakes active views work like an explicit allow-list of view-annotated properties, matching what Spring and many users already expected. - Performance note: maintainers re-ran benchmark combinations before merging and no longer considered the overhead meaningful enough to block the change.
- databind#1484
- Rationale: the 2.x default made unannotated properties included even when a view was active, which made
USE_GETTERS_AS_SETTERS: default tofalse-- earlier default was based on JAXB but is quite confusing for usersSORT_PROPERTIES_ALPHABETICALLY: default totrue(falsereally doesn't make much sense since it is unstable, and arbitrary based on JVM/JDK)
Changes, potential:
OVERRIDE_PUBLIC_ACCESS_MODIFIERS: potential default tofalse, since benefits of enabling seem small if not non-existent with 3.0- Rationale: forced
setAccessible(true)for already-public members was historically a reflection performance optimization, but that benefit becomes weaker if access paths move towardMethodHandles; keeping it enabled also increases the surface for JPMS / restricted-access failures. - Status: not changed for 3.0; databind#5074 remains open and the source still has this feature enabled by default as of current
3.x. - databind#5074
- Rationale: forced
DeserializationFeature
Changes completed:
FAIL_ON_UNKNOWN_PROPERTIES: default tofalse(TOP REQUEST by users) databind#493- Rationale: JSON producers and consumers often evolve independently. Failing on every unknown field makes compatible schema growth brittle, whereas skipping unknown values follows the open-content/forward-compatibility model requested by many users and used by comparable frameworks. A DoS concern was discussed and not considered practical because unknown values are skipped from the streaming parser, not materialized as trees.
READ_ENUMS_USING_TO_STRING: default totrue(instead offalsethat relies onname()) databind#4566- Rationale: for ordinary enums the result is the same because
Enum.toString()defaults toname(), butname()is final whiletoString()can be overridden. This gives enum types a natural annotation-free customization hook and was requested by multiple users (see maintainer comment). - Compatibility note: enums whose
toString()differs fromname()will change their JSON representation unless the mapper is configured back to Jackson 2.x behavior.java.time.temporal.ChronoUnitwas reported as one such affected JDK enum. - NOTE: this was further renamed/moved as
EnumFeature.READ_ENUMS_USING_TO_STRING(via databind#5079)
- Rationale: for ordinary enums the result is the same because
FAIL_ON_NULL_FOR_PRIMITIVES: default totrue(databind#4858)- Setting to
falsewould allowing coercion from JSON null into Java int, long and so on. For some users, and especially on "low null" languages like Kotlin, such coercion is rarely expected.
- Setting to
FAIL_ON_TRAILING_TOKENS: default totrue(databind#3406)- Rationale: with the 2.x default,
readValue()could successfully read the first value and silently ignore extra content after it. Failing by default catches malformed garbage, accidentally concatenated documents, and parsing-differential risks; enabling it during 3.0 work also exposed real backend regressions. - Compatibility note: callers intentionally doing partial/streaming reads may need to disable this feature.
- Rationale: with the 2.x default,
Changes planned but not made (or were reverted):
FAIL_ON_UNEXPECTED_VIEW_PROPERTIES: default remainsfalsefor 3.0- Feature added for databind#437, via databind#4275
SerializationFeature
Changes completed:
WRITE_ENUMS_USING_TO_STRING: default totrue(to keep symmetry between itsDeserializationFeature.READ_ENUMS_USING_TO_STRINGcounterpart) databind#4567- Rationale: same enum rationale as
READ_ENUMS_USING_TO_STRING:toString()preserves the default representation for vanilla enums while allowing custom enum wire names through an overridable method. - NOTE: this was further renamed/moved as
EnumFeature.WRITE_ENUMS_USING_TO_STRING(via databind#5080)
- Rationale: same enum rationale as
FAIL_ON_EMPTY_BEANS: default tofalsedatabind#3070- Rationale: an object with no visible properties has a clear JSON representation,
{}. Treating it as an exception surprised users and could obscure the real failure path in frameworks; users who want the old fail-fast diagnostic can explicitly re-enable the feature.
- Rationale: an object with no visible properties has a clear JSON representation,
FAIL_ON_ORDER_MAP_BY_INCOMPARABLE_KEY: was added in 2.19. Changed default in 3.0 via databind#4781- Rationale: when key ordering is requested, sorting via
TreeMapcan fail for non-Comparablekeys. Jackson 3 defaults this new guard tofalse, so incomparable-key maps fall back to original map iteration order instead of hard-failing, while callers who want the diagnostic exception can enable it. - See also databind#4773.
- Rationale: when key ordering is requested, sorting via
WRITE_DURATIONS_AS_TIMESTAMPS: change default tofalseto serialize as ISO-8601 String -- databind#4846- Rationale: numeric duration/period output is compact, but users and frameworks generally expect textual ISO-8601 duration representations by default. The major-version boundary allows Jackson to prefer the more interoperable human-readable shape.
- Note: renamed/moved as
DateTimeFeature.WRITE_DURATIONS_AS_TIMESTAMPS
- Writing date/time values as ISO-8601 Strings instead of (Java style) timestamps
WRITE_DATES_AS_TIMESTAMPS: change default tofalseto serialize as ISO-8601 String -- databind#4845- Rationale: numeric date/time timestamps are efficient, but the issues note that most users and frameworks expect strings. Jackson 3 therefore defaults to textual ISO-8601 output for date/time values.
- Note: renamed/moved as
DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS
WRITE_DATE_KEYS_AS_TIMESTAMPS: RETAIN default offalse-- no change- Note: renamed/moved as
DateTimeFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS
- Note: renamed/moved as
DateTimeFeature
Changes completed:
ONE_BASED_MONTHS: default totrue(3.0.0-rc3)- Rationale: this affects numeric handling of
java.time.Month. The Jackson 2.x zero-based behavior was retained for backwards compatibility, but it conflicts withMonth.getValue(), ISO-8601, and ordinary calendar convention; Jackson 3 changes numeric month values to one-based by default. MapperBuilder.configureForJackson2()sets this back tofalsefor Jackson 2.x compatibility.- https://github.com/FasterXML/jackson-databind/issues/5065
- Rationale: this affects numeric handling of
- Moved entries from
DeserializationFeature:ADJUST_DATES_TO_CONTEXT_TIME_ZONE,READ_DATE_TIMESTAMPS_AS_NANOSECONDS - Moved entries from
SerializationFeature(WRITE_DATE_xxxand similar)
Changes to other format backend defaults
Changes to CBOR defaults
CBORReadFeature:CBORReadFeature.DECODE_USING_STANDARD_NEGATIVE_BIGINT_ENCODING: change default totrueCBOR#582- Rationale: Jackson 2.x kept its legacy negative
BigIntegerencoding for compatibility, but that encoding is not the standard CBOR representation. Jackson 3 enables standard decoding by default for interoperability with spec-compliant CBOR implementations.
- Rationale: Jackson 2.x kept its legacy negative
CBORReadFeature.READ_UNDEFINED_AS_EMBEDDED_OBJECT: change default totrueCBOR#591- Rationale: when disabled, CBOR
undefinedis exposed asJsonToken.VALUE_NULL, making it indistinguishable from a real CBOR null. Jackson 3 exposes it asVALUE_EMBEDDED_OBJECTwith embedded valuenull, preserving the wire-level distinction.
- Rationale: when disabled, CBOR
CBORReadFeature.READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT: change default totrueCBOR#591- Rationale: when disabled, generic CBOR simple values are exposed as integer tokens, which makes them look like ordinary numbers. Jackson 3 exposes them as
VALUE_EMBEDDED_OBJECTvalues backed byCBORSimpleValue, so callers can distinguish them from real integers.
- Rationale: when disabled, generic CBOR simple values are exposed as integer tokens, which makes them look like ordinary numbers. Jackson 3 exposes them as
CBORWriteFeature:CBORWriteFeature.ENCODE_USING_STANDARD_NEGATIVE_BIGINT_ENCODING: change default totrueCBOR#582- Rationale: same interoperability reason as read-side negative
BigIntegerhandling; Jackson 3 writes the standard CBOR representation by default instead of the legacy Jackson encoding.
- Rationale: same interoperability reason as read-side negative
Changes to XML defaults
XmlWriteFeatureXmlWriteFeature.UNWRAP_ROOT_OBJECT_NODE: Change default totrueXML#725- Rationale: for a root
ObjectNodewith exactly one entry, using that entry's key as the root element name produces the expected XML root instead of a generic wrapper name.
- Rationale: for a root
XmlWriteFeature.WRITE_NULLS_AS_XSI_NIL: Change default totrueXML#727- Rationale: XML Schema defines
xsi:nilfor null-valued elements; enabling this makes null output explicit instead of relying on an ambiguous empty element.
- Rationale: XML Schema defines
XmlWriteFeature.AUTO_DETECT_XSI_TYPE: Change default totrueXML#728- Rationale: polymorphic XML type ids often need the standard
xsi:typeattribute, and this feature automatically maps logical property namexsi:typeto the proper XML Schema Instance namespace.
- Rationale: polymorphic XML type ids often need the standard
XmlWriteFeature.WRITE_XML_SCHEMA_CONFORMING_FLOATS: Change default totrueXML#729- Rationale: XML Schema and JAXB use
INF/-INFfor infinity values, while Java'sInfinity/-Infinitystrings are not understood by XML Schema-conforming readers.
- Rationale: XML Schema and JAXB use
Changes to Date/Time defaults
NOTE: see above for databind DateTimeFeature changes (including replacing some SerializationFeature/DeserializationFeatures), as well as default setting changes:
Changes to defaults, handling:
- The default rendering for UTC in 3.0 is now a trailing
Z, not a+00as in 2.x.- Implementation note: current
3.xhasDateTimeFeature.WRITE_UTC_AS_OFFSET, disabled by default, to represent the Jackson 3 behavior;MapperBuilder.configureForJackson2()enables it for 2.x-compatible mapper defaults.
- Implementation note: current
Proposed (but not completed) changes:
- Disable
lenientsetting (that is: default to "strict")
Changes to jackson-module-kotlin defaults
Kotlin module has a few proposed/accepted changes.
- Discussion of changes to default behavior in
Jackson 3.0(Parent/Overall discussion) -- and specific sub-issues:
Misc other default setting, behavior changes
JsonNode handling
DecimalNodecreation viaJsonNodeFactory: default to NOT truncating trailing zeroes (minimal trimming), becauseJsonNodeshould by default expose content as close to way it came.
MonthSerializer and MonthDeserializer behavior change (databind#5127)
- In 2.x,
java.time.Monthwas handled as plainEnumand was applied withEnum-related handling such asEnumFeature.WRITE_ENUMS_USING_INDEXorEnumFeature.WRITE_ENUMS_USING_TO_STRING. - Starting 3.x, along with
java-timemerging todatabindmodule, it's now handled like regular date/time classes, defaulting to number as value.- Note: numeric
Monthvalues are affected byDateTimeFeature.ONE_BASED_MONTHS; Jackson 3 defaults to one-based month numbers.
- Note: numeric
Using Jackson 2.x defaults in Jackson 3
For projects that still rely on Jackson 2.x behavior, Jackson 3 provides a builder method that configures an ObjectMapper with the older defaults where possible.
To create an ObjectMapper using Jackson 2.x defaults:
ObjectMapper mapper = JsonMapper.builderWithJackson2Defaults().build();
Note: This method does not (and cannot) fully replicate every Jackson 2.x default. Verify behavior in your application before relying on it.