Skip to content

JSTEP 4

Tatu Saloranta edited this page Jun 2, 2024 · 18 revisions

(Back to JSTEP page)

Replace checked JsonProcessingException with unchecked JacksonException

Author

Tatu Saloranta (@cowtowncoder)

Version history

  • 2024-06-02: update to indicate completion
  • 2019-02-10: first skeletal revision

Status

Complete as of Jackson 2.18

Background

Although choice of JsonProcessingException to extend IOException made sense originally, later developments -- in particular, Java 8 Streams and functional approach in general -- have made use of checked exceptions cumbersome for many use cases. And although there are minor benefits to being able to simply let underlying IOExceptions propagate through reading, actual work to catch and rethrow these are streaming API is quite modest (verified by brief investigation to number of actual stream-read methods).

But due to backwards compatibility concerns, change of base exception type has not been possible within 2.x releases.

Now that we are working on 3.0 we can, however, make this change. Compared to alternative of providing alternate subtypes of ObjectMapper, ObjectReader and/or ObjectWriter (ones that do not declare checked exceptions for methods), this leads to simpler API.

Proposal

New shared base exception

It is proposed that we add JacksonException that directly extends RuntimeException.

An alternative would be for it to extend UncheckedIOException (an existing JDK type): problem is that one can only be created if there is an actual IOException to wrap -- and this would only be true for streaming read/write methods, but not for exceptions Jackson itself throws.

Handling of stream-level IOExceptions from reading/writing

One thing to consider when wrapping low-level IOException from InputStream and OutputStream is whether to:

  1. Throw a new type of subtype of JacksonException (WrappedIOException), OR
  2. Wrap these as UncheckedIOExceptions and only make Jackson's "own" exception (thrown by it directly, not by wrapping) be subtypes of JacksonException

Although both approaches would work, the plan is to go with (1).

Mapping existing exception hierarchy to new hierarchy

First question on mapping is whether to use existing names, and to what degree. At extremes we could:

  1. Rename everything to optimal names (mostly get rid of "json" in names), or
  2. Keep all existing exception names, and at most add new types (approach similar to how 2.x developed)

We should probably do something in-between, to try to balance convenience of upgrade (if names remain mostly the same, only import statements need to be changed, see JSTEP-1) with the goal of improving naming in general.

Here are currently proposed changes to naming:

  • Streaming:
    • JsonProcessingException -> replace with JacksonException
      • JacksonException backported in 2.12 as the direct parent type of JsonProcessingException
    • JsonParseException -> StreamReadException
    • JsonGenerationException -> StreamWriteException
  • Databind:
    • JsonMappingException -> replace with DatabindException
      • DatabindException backported in 2.12 as the direct parent type of JsonMappingException

Open Issues

Currently (2.x) only databind-level (JsonMappingException) exceptions contain logical position within decoded/encoded content. But although streaming exceptions can not create such location in 2.x, with 3.0 we can change this through new context object that parser/generators have... so how should we expose such additional information? Perhaps there could/should be co-variant accessors for location information... ?

Relevant implementation issues: