Three Maven modules - jackson2-before (2.22.1), jackson3-after (3.2.1) and a coexistence module with BOTH majors on one classpath - so every claim in the two migration guides is executed rather than asserted. Paired class names make the before/after outputs directly diffable via run-all.sh. Confirms the guides on wire-format equivalence (10-case suite, zero mismatches), classpath coexistence and the collapse of four artifacts into one. Corrects nine points, including that enableDefaultTyping() is still present in Jackson 2.22.1 rather than removed in 2.16, and that the published "after" mapper snippet does not compile.
2.1 KiB
S02 — The exception hierarchy
Guide: https://ankurm.com/jackson-3-vs-jackson-2/ ("JacksonException Is Now Unchecked")
before/S02ExceptionHierarchy.java ·
after/S02ExceptionHierarchy.java
The guide calls this the most dangerous change and it is right. But the failure mode is
narrower than "every catch (IOException) silently stops catching", and the difference
decides how much of your codebase you actually have to audit.
Case A — the try block contains only Jackson calls. catch (IOException) becomes a
compile error: "exception java.io.IOException is never thrown in body of corresponding
try statement". The compiler finds these for you. No audit needed.
Case B — the try block also does real I/O. IOException is still reachable from the
I/O, so the catch block compiles, and it silently stops covering the Jackson call.
Nothing warns you. This is the shape that reaches production, and it is what the
example runs.
Output
Jackson 2 — jackson2-before
JsonProcessingException extends IOException : true
caught by catch (IOException) : JsonParseException
writeValueAsString declares : [class com.fasterxml.jackson.core.JsonProcessingException]
Jackson 3 — jackson3-after
JacksonException extends RuntimeException : true
JacksonException extends IOException : false
ESCAPED catch (IOException) : StreamReadException
caught by catch (Jackson...) : StreamReadException
writeValueAsString declares : [class tools.jackson.core.JacksonException] <- nothing checked
So the audit target is narrower and more specific than "grep for catch (IOException)":
it is catch (IOException) blocks that contain both I/O and a Jackson call. Those
are the ones the compiler cannot help with.
Note the last line of each. Jackson 3 still declares JacksonException on
writeValueAsString, but since it is unchecked, callers are not forced to handle it —
which is what lets Jackson calls sit inside lambdas without a wrapper.