Skip to content

feat: Improve Jackson 2 compatibility for Swagger mappers - #5334

Merged
ewaostrowska merged 3 commits into
3.0.0from
defaults-jackson2
Sep 30, 2026
Merged

ewaostrowska merged 3 commits into
3.0.0from
defaults-jackson2

Conversation

@ewaostrowska

Copy link
Copy Markdown
Contributor

Pull Request

Restores selected Jackson 2 behavior after the Jackson 3 migration.

Each MapperBuilder now calls configureForJackson2() before Swagger Core applies its own configuration.

The compatibility configuration also restores behavior that configureForJackson2() does not cover:

  • Accept lower-case and non-letter accessor names.
  • Fail when ordered map keys cannot be compared.

Changes

  • Apply Jackson 2 compatibility to all JSON, YAML, converter, and strict mappers.
  • Apply Swagger Core overrides after the Jackson 2 configuration.
  • Preserve enum output through Enum.toString().
  • Read enum values through Enum.name().
  • Preserve the strict mapper check for trailing tokens.
  • Keep plain BigDecimal output limited to public output mappers.
  • Remove separate manual configuration for Jackson 2 date and time behavior.

Known differences from Jackson 2

This PR does not restore all Jackson 2 behavior.

java.time.Month

Jackson 3 handles Month as a date and time type. Jackson 2 handled Month as a general enum.

This PR disables ONE_BASED_MONTHS. Therefore, numeric month values keep the Jackson 2 meaning:

  • 0 is January.
  • 11 is December.

The serialized type still changes:

  • Jackson 2 writes Month.JANUARY as "JANUARY".
  • The current Jackson 3 mapper writes Month.JANUARY as 0.

Text input such as "JANUARY" remains valid. The generated schema currently describes Month as a string.

Therefore, the serialized value and the generated schema use different types. A separate change is necessary to restore Jackson 2 string output.

Accessor acronyms

Jackson 3 removed the Jackson 2 legacy acronym rule. configureForJackson2() does not restore this rule.

The explicit accessor configuration accepts names such as getvalue() and get_value(). It does not restore legacy acronym conversion.

Examples:

Accessor Jackson 2 property Current property
getURL() url URL
getIPhone() iphone IPhone
isURL() url URL

This difference affects JSON, YAML, input names, and generated schema properties. A custom accessor strategy is necessary for full acronym compatibility.

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • ♻️ Refactor (non-breaking change)
  • 🧪 Tests
  • 📝 Documentation
  • 🧹 Chore (build or tooling)

Checklist

  • I have added/updated tests as needed
  • I have added/updated documentation where applicable
  • The PR title is descriptive
  • The code builds and passes tests locally
  • I have linked related issues (if any)

Screenshots / Additional Context

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The configuration ordering matches the stated compatibility goals and is comprehensively covered by focused tests.

Review effort: Balanced
Findings: None

What changed in this PR

Restores Jackson 2 compatibility defaults across Swagger Core mappers while retaining Swagger-specific overrides.

Changes:

  • Applies shared Jackson 2 compatibility and accessor behavior.
  • Preserves enum, date/time, decimal, ordering, and strict parsing policies.
  • Expands compatibility tests across public and strict mappers.
File Description
ObjectMapperFactory.java Centralizes compatibility configuration and mapper-specific overrides.
ObjectMapperFactoryTest.java Adds broad compatibility and strict-mapper coverage.
OrderedMapKeyCompatibilityTest.java Extends ordered-map validation to the strict mapper.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@daniel-kmiecik

daniel-kmiecik commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Code review

Found 2 issues:

  1. buildStrictGenericObjectMapper UTC date format silently changed from Z to +00:00

Previously this method did not call any compatibility setup, so WRITE_UTC_AS_OFFSET remained false (Jackson 3 default) and UTC timestamps serialized with a Z suffix. After this PR, configureJackson2Compatibility() is applied to the strict mapper, setting WRITE_UTC_AS_OFFSET=true via configureForJackson2(). The test that enforced the old behavior (assertFalse(mapper.isEnabled(DateTimeFeature.WRITE_UTC_AS_OFFSET), name)) was silently removed. MIGRATION.md documents the flag value globally but does not call out that the strict mapper specifically changes its UTC format — consumers relying on it to produce Z-suffix examples will see a silent behavioral change.

public static ObjectMapper buildStrictGenericObjectMapper() {
JsonMapper.Builder builder = JsonMapper.builder();
configureJackson2Compatibility(builder);
configureSwaggerPolicy(builder);
builder.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true);

  1. buildStrictGenericObjectMapper placed in the publicMappers data provider

ObjectMapperFactoryTest maintains a strict publicMappers / strictMapper provider split — the strict mapper is explicitly kept separate because it intentionally omits MapperCustomizer registration. Adding it to publicMappers in OrderedMapKeyCompatibilityTest breaks that convention: any future test added to that provider with the expectation it covers only public-API mappers will silently also run against the internal strict mapper.

mapper("createYaml(false)", () -> ObjectMapperFactory.createYaml(false)),
mapper("createYaml(true)", () -> ObjectMapperFactory.createYaml(true)),
mapper("buildStrictGenericObjectMapper", ObjectMapperFactory::buildStrictGenericObjectMapper)
};
}

@ewaostrowska

Copy link
Copy Markdown
Contributor Author

@daniel-kmiecik

  1. Is intended behavior to align it with Jackson 2 behavior. I have double checked and it behaves the same on master.
  2. Point 2 is fair - publicMappers should not contain the strict mapper

@ewaostrowska
ewaostrowska merged commit 155b349 into 3.0.0 Sep 30, 2026
6 checks passed
@ewaostrowska
ewaostrowska deleted the defaults-jackson2 branch September 30, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants