| Annotation | Purpose |
@JsonProperty("name") | Rename a field in JSON |
@JsonIgnore | Exclude a field from serialisation and deserialisation |
@JsonIgnoreProperties({"f1","f2"}) | Ignore named fields on the class |
@JsonIgnoreProperties(ignoreUnknown=true) | Silently ignore unknown JSON fields |
@JsonInclude(Include.NON_NULL) | Omit null fields from output |
@JsonInclude(Include.NON_EMPTY) | Omit null, empty collections, empty strings |
@JsonFormat(pattern="yyyy-MM-dd") | Date/time format for a field |
@JsonCreator | Mark a constructor or factory method for deserialisation |
@JsonValue | Serialise the object as the return value of this method |
@JsonAlias({"alt1","alt2"}) | Accept alternative field names during deserialisation |
@JsonUnwrapped | Inline nested object's fields into the parent JSON object |
@JsonAnyGetter / @JsonAnySetter | Serialise/deserialise dynamic/unknown fields into a Map |
@JsonTypeInfo / @JsonSubTypes | Polymorphic type handling |
import com.fasterxml.jackson.annotation.*;
import java.time.LocalDate;
import java.util.Map;
import java.util.HashMap;
@JsonIgnoreProperties(ignoreUnknown = true) // tolerate extra JSON fields
@JsonInclude(JsonInclude.Include.NON_NULL) // omit null fields globally
public class User {
@JsonProperty("user_id") // output field name: "user_id"
private Long id;
@JsonProperty("full_name")
private String name;
@JsonIgnore // never serialised or deserialised
private String passwordHash;
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate createdAt;
@JsonAlias({"emailAddress", "email_address"}) // accept any of these on input
private String email;
// Dynamic extra fields
private Map<String, Object> extra = new HashMap<>();
@JsonAnyGetter
public Map<String, Object> getExtra() { return extra; }
@JsonAnySetter
public void addExtra(String key, Object value) { extra.put(key, value); }
// Required no-arg constructor for Jackson (or use @JsonCreator on constructor)
public User() {}
}
Use @JsonCreator to tell Jackson which constructor or static factory method to use when deserialising. Pair it with @JsonProperty to map JSON fields to constructor parameters.
import com.fasterxml.jackson.annotation.*;
public final class Money {
private final long cents;
private final String currency;
@JsonCreator
public Money(
@JsonProperty("cents") long cents,
@JsonProperty("currency") String currency) {
this.cents = cents;
this.currency = currency;
}
// Static factory variant
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static Money fromString(String value) {
String[] parts = value.split(":");
return new Money(Long.parseLong(parts[0]), parts[1]);
}
@JsonValue
public String toValue() { return cents + ":" + currency; } // serialised as "1234:USD"
public long getCents() { return cents; }
public String getCurrency() { return currency; }
}
If you cannot add Jackson annotations to a class (e.g., a library type), create a mixin class/interface that mirrors the target's API with annotations, then register it.
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
// Third-party class you cannot touch
class ThirdPartyUser {
public Long id;
public String name;
public String internalSecret; // should not be exposed
public String firstName;
public String lastName;
}
// Mixin — abstract class mirroring ThirdPartyUser
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class ThirdPartyUserMixin {
@JsonIgnore
abstract String getInternalSecret(); // exclude this field
@JsonProperty("full_name")
abstract String getName(); // rename name → full_name
// Inline firstName/lastName instead of as nested object
@JsonUnwrapped
// (if they were in a nested Name object)
abstract String getFirstName();
}
// Register the mixin
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(ThirdPartyUser.class, ThirdPartyUserMixin.class);
ThirdPartyUser user = new ThirdPartyUser();
user.id = 1L; user.name = "Alice"; user.internalSecret = "secret-token";
String json = mapper.writeValueAsString(user);
// {"id":1,"full_name":"Alice"} — internalSecret excluded
Mixins are ideal for serialising JDK types (like java.security.Principal), third-party domain objects, or any class where you want to keep Jackson annotations out of your domain model.
import com.fasterxml.jackson.annotation.*;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // embed a "type" field
include = JsonTypeInfo.As.PROPERTY,
property = "type") // the discriminator field name
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
abstract class Animal {
public String name;
}
class Dog extends Animal {
public String breed;
}
class Cat extends Animal {
public boolean indoor;
}
// Serialise
ObjectMapper mapper = new ObjectMapper();
Animal dog = new Dog(); ((Dog)dog).name = "Rex"; ((Dog)dog).breed = "Labrador";
String json = mapper.writeValueAsString(dog);
// {"type":"dog","name":"Rex","breed":"Labrador"}
// Deserialise — Jackson reads "type" field and creates the right subtype
Animal restored = mapper.readValue(json, Animal.class);
System.out.println(restored.getClass().getSimpleName()); // Dog
Jackson's SerializationConfig and JavaType system lets you inspect how a class will be serialised without actually serialising it.
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.*;
ObjectMapper mapper = new ObjectMapper();
// Get the serialisation bean description for a class
JavaType type = mapper.getTypeFactory().constructType(User.class);
BeanDescription desc = mapper.getSerializationConfig()
.introspect(type);
// List all serialisable properties
for (BeanPropertyDefinition prop : desc.findProperties()) {
System.out.printf("Field: %-20s JSON name: %s%n",
prop.getInternalName(), prop.getName());
}
// Check if a field would be ignored
AnnotationIntrospector ai = mapper.getSerializationConfig().getAnnotationIntrospector();