# Jackson 2 To 3 Migration

> Migrate a Java project from Jackson 2 to Jackson 3. Use this skill whenever the user wants to upgrade Jackson, mentions jackson-databind, com.fasterxml.jackson imports, or asks about moving from Jackson 2.x to 3.x. Also trigger when the user shares Java/Kotlin code or build files (pom.xml, build.gradle) containing Jackson 2 dependencies and asks to update, modernize, or migrate them. This skill covers import renaming, dependency GroupId changes, API migrations, IOException-to-JacksonException changes, ObjectMapper builder patterns, BOM usage in multi-module Maven projects, and OpenRewrite recipe usage.

- Skill: `litsec/jackson-2-to-3-migration` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add litsec/jackson-2-to-3-migration`
- Raw SKILL.md: https://api.skillmd.com/api/skills/litsec/jackson-2-to-3-migration/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: litsec (https://skillmd.com/u/litsec)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/litsec/jackson-2-to-3-migration

---


# Jackson 2 → 3 Migration Skill

## Overview

Jackson 3 is a major version with **breaking changes**. The key ones are:

1. Root Java package: `com.fasterxml.jackson` → `tools.jackson`
2. Maven/Gradle GroupIds changed to match
3. Many methods that previously declared `throws IOException` now declare `throws JacksonException`
4. `ObjectMapper` builder pattern is now strongly preferred
5. A Jackson BOM is available for centralized version management

Before doing anything, read the full reference:
→ `references/jackson2-to-3-changes.md`

---

## Migration Strategy

Choose the right approach based on what the user has:

| User provides | Approach |
|---|---|
| `pom.xml` / `build.gradle` only | Update dependencies, add BOM, explain code changes needed |
| Java/Kotlin source files | Migrate imports + update API usage + fix exception handling |
| Both build + source | Do both |
| Asking how to automate | Recommend OpenRewrite recipe |

---

## Step 1: Assess the Codebase

Scan the project for:
1. **Dependencies**: all `com.fasterxml.jackson.*` groupIds in POM files
2. **Imports**: all `import com.fasterxml.jackson.*` in Java/Kotlin sources
3. **Exception handling**: methods catching `IOException` from Jackson calls — these may need
   to catch `JacksonException` (or both during a transition period)
4. **Throws declarations**: methods declaring `throws IOException` only because of Jackson —
   these can be narrowed to `throws JacksonException`
5. **API usage patterns**: `new ObjectMapper()` configured imperatively, deprecated APIs

---

## Step 2: Update POM Files

Read `references/jackson2-to-3-changes.md` → section "Maven / Gradle Dependency Changes"
for the full GroupId mapping table.

### Look up the latest Jackson 3 version

Before writing any version number, query Maven Central for the latest stable release of
the BOM:

```bash
curl -s "https://search.maven.org/solrsearch/select?q=g:tools.jackson+AND+a:jackson-bom&rows=1&wt=json" \
  | jq -r '.response.docs[0].latestVersion'
```

Use the returned version throughout. Do **not** default to `3.0.0` without checking —
there may be a newer patch or minor release available.

### Multi-module projects — use the Jackson BOM

In the **root POM's** `<dependencyManagement>` section, replace all individual Jackson version
declarations with a single BOM import:

```xml
<dependencyManagement>
    <dependencies>
        <!-- Jackson BOM — manages versions for all tools.jackson.* artifacts -->
        <dependency>
            <groupId>tools.jackson</groupId>
            <artifactId>jackson-bom</artifactId>
            <version><!-- latest from Maven Central --></version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
```

Then in child modules, declare Jackson dependencies **without** a `<version>` tag:

```xml
<dependency>
    <groupId>tools.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <!-- no version — managed by BOM in root POM -->
</dependency>
```

### Single-module projects

Add the BOM to `<dependencyManagement>` in the same POM, then remove `<version>` from
individual Jackson dependencies below it.

### Remove old Jackson 2 entries

Remove all `com.fasterxml.jackson.*` dependencies. Check for:
- Direct dependencies in `<dependencies>`
- Managed versions in `<dependencyManagement>`
- `<exclusions>` that reference old Jackson coordinates (update those too)

---

## Step 3: Update Java/Kotlin Imports

Mechanical find-and-replace across all source files:

```
com.fasterxml.jackson  →  tools.jackson
```

This covers all sub-packages automatically. Offer to apply this to any files the user provides.

---

## Step 4: Update Exception Handling

This is the most commonly missed change in Jackson 3.

### What changed

Many Jackson methods that previously declared `throws IOException` now declare
`throws JacksonException`. `JacksonException` extends `IOException`, so **existing code
compiles** — but catch blocks and throws declarations may need updating for correctness
and intent clarity.

Read `references/jackson2-to-3-changes.md` → section "Exception Changes" for the full
list of affected methods.

### Catch block patterns to update

```java
// Jackson 2 — catching broad IOException from Jackson operations
try {
    MyType obj = mapper.readValue(json, MyType.class);
} catch (IOException e) {
    // handle
}

// Jackson 3 — prefer catching JacksonException for Jackson-specific errors
try {
    MyType obj = mapper.readValue(json, MyType.class);
} catch (JacksonException e) {
    // handle Jackson-specific parse/mapping errors
}
```

### Throws declarations to narrow

```java
// Jackson 2
public MyType parse(String json) throws IOException {
    return mapper.readValue(json, MyType.class);
}

// Jackson 3 — can narrow to JacksonException
public MyType parse(String json) throws JacksonException {
    return mapper.readValue(json, MyType.class);
}
```

### When to keep IOException

Keep `throws IOException` or `catch (IOException e)` when the method also does real I/O
(e.g. reading from a `File`, `InputStream`, network stream) alongside Jackson calls.
In that case both `IOException` and `JacksonException` are valid — `JacksonException`
extends `IOException` so the existing `IOException` catch still works, but you may want
to separate them:

```java
try {
    return mapper.readValue(inputStream, MyType.class);
} catch (JacksonException e) {
    throw new ParseException("Invalid JSON", e);
} catch (IOException e) {
    throw new StorageException("Could not read stream", e);
}
```

### New import needed

```java
import tools.jackson.core.JacksonException;
```

---

## Step 5: Update API Usage

Read `references/jackson2-to-3-changes.md` → section "API Changes" for full details.

### ObjectMapper — prefer builder

```java
// Jackson 2 style (imperative config)
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new JavaTimeModule());

// Jackson 3 style (builder)
ObjectMapper mapper = JsonMapper.builder()
    .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    .addModule(new JavaTimeModule())
    .build();
```

### Check for removed deprecated APIs

See reference file section "Removed APIs". If any are present in user code, suggest replacements.

---

## Step 6: Automation Option (OpenRewrite)

For large codebases, recommend OpenRewrite — it handles dependencies, imports, and many API
migrations automatically. Show the snippet from the reference file's "OpenRewrite Recipe" section.

Note: OpenRewrite does **not** currently handle the `IOException` → `JacksonException` catch
block narrowing — that must be done manually or with IDE inspections.

---

## Step 7: Warn About Spring Boot Compatibility

⚠️ **Critical check**: Ask or check if they use Spring Boot.

- **Spring Boot 3.x** → uses Jackson 2. Do **not** upgrade Jackson independently.
- **Spring Boot 4.x** → uses Jackson 3. Upgrading Jackson independently is appropriate.

If they're on Spring Boot 3.x and want Jackson 3, they need to upgrade Spring Boot first.

---

## Output Format

When migrating files, show a clear before/after diff or provide the updated file. For
multi-file migrations, create updated versions of each file. Always summarize:

1. POMs changed and what BOM entry was added
2. Source files changed and how many import lines replaced
3. Exception handling changes made or recommended
4. Any manual follow-up needed (removed APIs, annotation behavior changes)

