Jackson databind

repository·3.x·Indexed 25 days ago

https://github.com/fasterxml/jackson-databind

A Java library providing general-purpose data-binding functionality and a tree-model for processing JSON and other data formats. Built on the Jackson Streaming API, it enables serialization and deserialization between JSON and POJOs using ObjectMapper, JsonMapper, and Jackson Annotations. Supports JDK 8+ (2.x) and JDK 17+ (3.x), with specific Android SDK compatibility for versions 2.14 through 3.0.

Tokens
4.5K
Snippets
11
Records
15
Agent score
37%

What's inside jackson-databind

  1. Install jackson-databind via Maven

    3.x

    To use Jackson databind in a Maven project, add the jackson-databind dependency. It is recommended to use the jackson-bom to ensure all Jackson dependencies (like jackson-core and jackson-annotations) are version-aligned. For Jackson 3.x, the Java package is tools.jackson.databind.

    <properties>
      ...
      <!-- Use the latest version whenever possible. -->
      <jackson.version>3.0.0</jackson.version>
      ...
    </properties>
    
    <dependencies>
      ...
      <dependency>
        <groupId>tools.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
      </dependency>
      ...
    </dependencies>
  2. Deserialize using the Builder pattern

    3.x

    To support deserialization into objects that use the Builder pattern, follow these steps:

    1. Annotate the target class with @JsonDeserialize(builder = YourBuilder.class).
    2. Annotate the inner Builder class with @JsonPOJOBuilder.

    If your builder uses a different method name than build() or a different prefix than the default, configure it via @JsonPOJOBuilder(buildMethodName = "...", withPrefix = "...").

    To handle different JSON property names within the builder, use @JsonProperty on the builder's fields. If a property has multiple possible names in the JSON, use @JsonAlias (note: @JsonAlias requires a corresponding @JsonProperty to be present).

    @JsonDeserialize(builder = Person.Builder.class)
    public class Person {
        private final String name;
        private final Integer age;
    
        @JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
        static class Builder {
            @JsonProperty("known_as")
            @JsonAlias({"identifier", "first_name"})
            String name;
            Integer age;
    
            Builder setName(String name) { this.name = name; return this; }
            Builder setAge(Integer age) { this.age = age; return this; }
    
            public Person create() { return new Person(name, age); }
        }
    }
  3. Configure ObjectMapper using the Builder pattern

    3.x

    In Jackson 3.x, ObjectMapper instances are immutable and thread-safe, requiring the use of the JsonMapper.builder() pattern for configuration. You can enable or disable various features for serialization, deserialization, and low-level JSON parsing/generation.

    Commonly used features include:

    • SerializationFeature: Controls how JSON is written (e.g., INDENT_OUTPUT for pretty-printing).
    • DeserializationFeature: Controls how JSON is read (e.g., FAIL_ON_UNKNOWN_PROPERTIES).
    • JsonReadFeature / JsonWriteFeature: Low-level JSON-specific settings (e.g., allowing comments or single quotes).
    • StreamReadFeature / StreamWriteFeature: Format-agnostic settings.
    // SerializationFeature for changing how JSON is written
    // to enable standard indentation ("pretty-printing"):
    // to allow serialization of "empty" POJOs (no properties to serialize)
    // to write java.util.Date, Calendar as number (timestamp):
    // DeserializationFeature for changing how JSON is read as POJOs:
    // to prevent exception when encountering unknown property:
    // to allow coercion of JSON empty String ("") to null Object value:
    
    ObjectMapper mapper = JsonMapper.builder()
        .enable(SerializationFeature.INDENT_OUTPUT)
        .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
        .disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
        .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
    
        // StreamReadFeatures for configuring parsing settings:
        // to allow C/C++ style comments in JSON (non-standard, disabled by default)
        // to allow (non-standard) unquoted field names in JSON:
        // to allow use of apostrophes (single quotes), non standard
        .configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true)
        .configure(JsonReadFeature.ALLOW_UNQUOTED_PROPERTY_NAMES, true)
        .configure(JsonReadFeature.ALLOW_SINGLE_QUOTES, true)
    
        // JsonWriteFeature for configuring low-level JSON generation:
        // to force escaping of non-ASCII characters:
        .configure(JsonWriteFeature.ESCAPE_NON_ASCII, true)
        
        .build();
  4. Collect multiple deserialization errors

    3.x

    Instead of failing on the first error, you can configure Jackson to collect multiple deserialization problems (such as type mismatches or unknown properties) into a single exception. This is useful for validation scenarios where you want to report all errors to a client at once.

    Key Details:

    • Method: Use ObjectReader.problemCollectingReader() to create a reader that collects errors.
    • Execution: Call reader.readValueCollectingProblems(json) to perform the deserialization.
    • Error Handling: Caught errors are thrown as a DeferredBindingException. You can iterate through ex.getProblems() to access CollectedProblem objects.
    • Problem Details: Each CollectedProblem provides .getPath() (using JSON Pointer notation, RFC 6901), .getMessage(), and .getRawValue().
    • Limits: By default, Jackson collects up to 100 problems to prevent DoS attacks. You can customize this limit using problemCollectingReader(int limit).
    • Limitations: Structural/malformed JSON errors (e.g., missing braces) will still cause immediate failure. If errors are collected, the partial result is not returned; only the exception is thrown.
    ObjectMapper mapper = new JsonMapper();
    ObjectReader reader = mapper.readerFor(Order.class).problemCollectingReader();
    
    try {
        Order result = reader.readValueCollectingProblems(json);
        // worked fine
    } catch (DeferredBindingException ex) {
        System.out.println("Found " + ex.getProblems().size() + " problems:");
        for (CollectedProblem problem : ex.getProblems()) {
            System.out.println(problem.getPath() + ": " + problem.getMessage());
            // Can also access problem.getRawValue() to see what the bad input was
        }
    }
  5. Handle Generic Collections with TypeReference

    3.x

    When deserializing generic collections like List or Map, Java's type erasure prevents automatic type detection. You must use a TypeReference to specify the actual generic type.

    // For simple types, Map.class or List.class works if values are simple
    Map<String, Integer> scoreByName = mapper.readValue(jsonSource, Map.class);
    
    // For generic POJO collections, use TypeReference
    Map<String, ResultValue> results = mapper.readValue(jsonSource, 
       new TypeReference<Map<String, ResultValue>>() { } );
  6. Serialize and deserialize POJOs

    3.x

    The most common use case is converting between JSON and Plain Old Java Objects (POJOs). Use ObjectMapper for data-binding. You can create a default instance or use the JsonMapper.builder() pattern for custom configuration.

    // POJO definition
    public class MyValue {
      public String name;
      public int age;
    }
    
    // Setup mapper
    ObjectMapper mapper = new ObjectMapper();
    // Or with builder
    ObjectMapper mapper = JsonMapper.builder().build();
    
    // Deserialize (JSON to Object)
    MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
    value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);
    
    // Serialize (Object to JSON)
    mapper.writeValue(new File("result.json"), myResultObject);
    String jsonString = mapper.writeValueAsString(myResultObject);
    byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
  7. Rename properties using @JsonProperty

    3.x

    Use the @JsonProperty annotation to map a JSON property name to a specific Java field, getter, or setter. This is useful when the JSON key differs from the Java field name. You only need to annotate either the getter or the setter.

    public class MyBean {
       private String _name;
    
       // without annotation, we'd get "theName", but we want "name":
       @JsonProperty("name")
       public String getTheName() { return _name; }
    
       // note: it is enough to add annotation on just getter OR setter;
       // so we can omit it here
       public void setTheName(String n) { _name = n; }
    }
  8. Configure the maximum number of collected problems

    3.x

    You can limit the number of deserialization problems Jackson collects before giving up. This is useful for controlling memory usage or preventing DoS-style attacks with large payloads containing many errors.

    Use problemCollectingReader(int limit) on your ObjectReader to set the threshold.

    ObjectReader reader = mapper.readerFor(Order.class).problemCollectingReader(10); // limit to 10
  9. Use custom constructors or factory methods with @JsonCreator

    3.x

    Jackson does not require a default (no-argument) constructor. You can use @JsonCreator to instruct Jackson to use a specific constructor or a static factory method for deserialization. When using a constructor, use @JsonProperty on the arguments to map JSON keys to constructor parameters. This is ideal for creating immutable objects.

    public class CtorBean
    {
      public final String name;
      public final int age;
    
      @JsonCreator // constructor can be public, private, whatever
      private CtorBean(@JsonProperty("name") String name,
        @JsonProperty("age") int age)
      {
          this.name = name;
          this.age = age;
      }
    }
    
    // Alternatively, using a factory method:
    public class FactoryBean
    {
        // fields etc omitted for brevity
    
        @JsonCreator
        public static FactoryBean create(@JsonProperty("name") String name) {
          // construct and return an instance
        }
    }
  10. Convert between POJOs using convertValue()

    3.x

    Jackson can perform arbitrary POJO-to-POJO conversions by conceptually writing a POJO to JSON and then binding that JSON to a new type. This is more efficient than actual JSON generation as it uses an intermediate representation. This is useful for:

    • Converting List<Integer> to int[].
    • Converting a POJO to a Map<String, Object> and vice versa.
    • Decoding Base64 strings into byte[].
    ResultType result = mapper.convertValue(sourceObject, ResultType.class);
    
    // Examples:
    // Convert from List<Integer> to int[]
    List<Integer> sourceList = ...;
    int[] ints = mapper.convertValue(sourceList, int[].class);
    
    // Convert a POJO into Map!
    Map<String,Object> propertyMap = mapper.convertValue(pojoValue, Map.class);
    
    // decode Base64!
    String base64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz";
    byte[] binary = mapper.convertValue(base64, byte[].class);