Skip to content

Deserializer Discovery 2.x

Tatu Saloranta edited this page Jan 29, 2024 · 19 revisions

Discovering Deserializers in Jackson 2.x

This page describes the process of discovering JsonDeserializers in Jackson 2.x

General

JsonDeserializers are needed for reading JSON (and other supported formats) from JsonParser and constructing desired Java Objects. Discovery process is initiated by 3 entities:

  1. ObjectMapper to locate deserializer to use for target type indicated for readValue() method (and readValues(), convertValue())
  2. ObjectReader (similar to ObjectMapper)
  3. Deserializers themselves, to locate "child deserializers": for example when deserializing Lists, deserializer for elements contained is separate from deserializer for List itself (and similarly for other structured types like java.util.Maps, Arrays, POJOs)

Discovery process for these cases is almost identical (and in fact, (1) and (2) are identical), differing only in that for (3), contextualization (via method DeserializationContext._handleSecondaryContextualization(...)) passes referring property definition (BeanProperty) whereas one does not exist for "root" values.

Deserializer Contextualization vs Resolution

Two terms for deserializers (as defined by ResolvableDeserializer and ContextualDeserializer) are:

  1. Resolution is needed to handle cyclic dependencies between deserialializers: this is mostly relevant for BeanDeserializer.
  2. Contextualization is needed to give initially context-free (only based on Type) deserializers access to annotations added to property (that is, on Field and/or Method and/or Constructor parameter).

Understanding this processing is not strictly necessary to understand call flow, but is useful to know since these are referenced in couple of places.

High-level call sequence

ObjectMapper to DeserializationContext

The first step to trigger discovery is ObjectMapper (and other entities mentioned earlier) calling one of:

  1. Explicit findXxxDeserializer() method such as findRootValueDeserializer()
  2. One of readXxxValue() convenience methods like readValue() which will need to call one of findXxxDeserializer() methods

Either way method like findRootValueDeserializer() is called; and this is the call path we will focus on.

DeserializationContext to DeserializerCache

Big part of actual discovery is handled by DeserializerCache: the main entry point is method findValueDeserializer(). It will not only handle caching of constructed and resolved (but not contextualized) deserializers but also:

  1. Mapping of abstract Java types (as well as Map and Collection types) into concrete (for abstract) and default (Map, Collection) implementation types
  2. Handling annotations on Java types (Classes) that directly indicate JsonDeserializer to use (@JsonDeserialize(using = IMPL_CLASS))
  3. Refinement of actual type to use (@JsonDeserialize(as = CONCRETE_TYPE))
  4. Use of "converting" deserializers indicated by annotation (and directly constructing StdDelegatingDeserializer as needed)
  5. Calling appropriate method of configured DeserializerFactory to actually construct JsonDeserializer to use
  6. Resolution of ResolveDeserializers, avoiding StackOverflowError for cyclic types

DeserializerCache to DeserializerFactory

Clone this wiki locally