Mastering Dynamic JSON Fields with Jackson’s @JsonAnyGetter and @JsonAnySetter

When a JSON payload contains unpredictable fields, Jackson’s @JsonAnySetter and @JsonAnyGetter let you capture those dynamic properties into a Map during deserialization and seamlessly emit them during serialization, eliminating the need for rigid POJOs.

Architecture Digest
Architecture Digest
Architecture Digest
Mastering Dynamic JSON Fields with Jackson’s @JsonAnyGetter and @JsonAnySetter

Problem

When deserializing JSON from APIs, the response may contain fields that are not known at compile time. Hard‑coding all possible properties in a Java class is impractical.

Using @JsonAnySetter for deserialization

The annotation marks a method that Jackson calls for each unknown property. The method typically stores the key‑value pair in a Map<String, Object> (e.g., additionalProperties).

public void addAdditionalProperty(String key, Object value) {
    additionalProperties.put(key, value);
}

Example JSON:

{
  "name": "豆瓣酱",
  "spicy": true,
  "limited_edition": "yes",
  "extra_notes": "只在冬天卖"
}

If the POJO defines only name and spicy, the unknown fields are collected into the map.

Complete deserialization example

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;

public class Person {
    private String name;
    private int age;
    private Map<String, Object> additionalProperties = new HashMap<>();

    @JsonAnySetter
    public void addAdditionalProperty(String key, Object value) {
        this.additionalProperties.put(key, value);
    }

    public Map<String, Object> getAdditionalProperties() {
        return additionalProperties;
    }
    // getters and setters for name and age omitted for brevity
}

public class Main {
    public static void main(String[] args) throws Exception {
        String json = "{\"name\":\"John\",\"age\":30,\"address\":\"123 Street\",\"nickname\":\"Johnny\"}";
        ObjectMapper mapper = new ObjectMapper();
        Person person = mapper.readValue(json, Person.class);
        System.out.println("Name: " + person.name);
        System.out.println("Age: " + person.age);
        System.out.println("Additional Properties: " + person.getAdditionalProperties());
    }
}

Output:

Name: John
Age: 30
Additional Properties: {address=123 Street, nickname=Johnny}

Using @JsonAnyGetter for serialization

The annotation marks a method that returns a map of dynamic properties. During serialization Jackson flattens the map entries into top‑level JSON fields.

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
    return additionalProperties;
}

Complete serialization example

public class Person {
    private String name;
    private int age;
    private Map<String, Object> additionalProperties = new HashMap<>();

    @JsonAnySetter
    public void addAdditionalProperty(String key, Object value) {
        additionalProperties.put(key, value);
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return additionalProperties;
    }
    // constructors, getters, setters omitted
}

public class Main {
    public static void main(String[] args) throws Exception {
        Person person = new Person("John", 30);
        person.addAdditionalProperty("address", "123 Street");
        person.addAdditionalProperty("nickname", "Johnny");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(person);
        System.out.println(json);
    }
}

Result:

{"name":"John","age":30,"address":"123 Street","nickname":"Johnny"}

When to use

These annotations are ideal for JSON structures where the set of fields can change at runtime, such as configuration objects, plugin descriptors, or any payload that may carry extra key‑value pairs beyond the core model.

Key points and caveats

The map must be mutable (e.g., HashMap) because Jackson adds entries during deserialization.

Only one method in a class should be annotated with @JsonAnySetter; multiple such methods lead to undefined behavior.

The getter annotated with @JsonAnyGetter should return the same map used by the setter to keep serialization consistent.

If you prefer to ignore unknown properties entirely, configure the ObjectMapper with

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

instead of using the annotations.

JavaserializationJSONjacksonDeserializationJsonAnySetterDynamicPropertiesJsonAnyGetter
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.