Java JSON com Jackson
Analise, gere e vincule JSON em Java com a biblioteca Jackson — ObjectMapper e data binding.
Jackson é a biblioteca JSON padrão de facto para Java. O JDK não fornece nenhuma API JSON própria, então praticamente toda aplicação Spring, Quarkus ou Micronaut recorre ao Jackson para converter objetos Java em texto JSON e vice-versa. Seu ponto de entrada é uma única classe versátil — ObjectMapper — que lida com as duas tarefas que você sempre precisa: serialização (objeto Java → JSON) e desserialização (JSON → objeto Java).
Se você é novo em JSON com Java, comece com a introdução ao JSON; para uma biblioteca alternativa mais leve, veja o capítulo sobre Gson. Esta página aborda como adicionar o Jackson, os três níveis de sua API, data binding, o modelo de árvore e as anotações que controlam o mapeamento.
Adicionando Jackson ao seu projeto
Jackson não faz parte do JDK, então você o declara como uma dependência. O artefato jackson-databind inclui os outros dois módulos principais (jackson-core e jackson-annotations) transitivamente.
<!-- Maven -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>// Gradle
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'As três formas de trabalhar com JSON
Jackson expõe os mesmos dados de três maneiras diferentes. Escolha o nível que melhor se adapta à tarefa:
| Abordagem | Tipo principal | Use quando |
|---|---|---|
| Data binding | ObjectMapper.readValue / writeValue | Você tem um POJO ou record que espelha o JSON — o caso mais comum |
| Modelo de árvore | JsonNode via readTree | A estrutura é dinâmica ou você só precisa de alguns campos |
| Streaming | JsonParser / JsonGenerator | Documentos grandes onde não é possível manter toda a árvore na memória |
Data binding é o que você usa 90% do tempo; os outros dois são alternativas de escape.
Data binding: objetos para JSON e volta
ObjectMapper.writeValueAsString serializa qualquer objeto Java lendo seus getters (ou componentes de record); readValue faz o inverso, associando nomes de campos JSON aos seus campos. Um record é o destino mais limpo — o Jackson 2.12+ se vincula ao seu construtor canônico automaticamente, então você não precisa escrever getters nem um construtor sem argumentos.
import com.fasterxml.jackson.databind.ObjectMapper;
record User(String name, int age, boolean active) {}
ObjectMapper mapper = new ObjectMapper();
// serialize: Java -> JSON
User ada = new User("Ada", 36, true);
String json = mapper.writeValueAsString(ada);
// {"name":"Ada","age":36,"active":true}
// deserialize: JSON -> Java
User back = mapper.readValue(json, User.class);
System.out.println(back.name()); // AdaPara coleções e genéricos, passe ao Jackson uma TypeReference para que o tipo do elemento sobreviva ao apagamento de tipos:
import com.fasterxml.jackson.core.type.TypeReference;
List<User> users = mapper.readValue(jsonArray, new TypeReference<List<User>>() {});O modelo de árvore: quando a estrutura é desconhecida
Quando você não tem (ou não quer) uma classe correspondente, analise o JSON em uma árvore genérica JsonNode e navegue por chave. Isso espelha o que um parser manual de Map/List faz, mas com accessors com conhecimento de tipo como asInt() e asText().
import com.fasterxml.jackson.databind.JsonNode;
JsonNode root = mapper.readTree(json);
String name = root.get("name").asText();
int age = root.get("age").asInt();
JsonNode first = root.get("languages").get(0); // array access by indexControlando o mapeamento com anotações
Os nomes de campos JSON raramente correspondem perfeitamente às convenções Java. Algumas anotações preenchem essa lacuna sem alterar os nomes dos seus campos:
| Anotação | Efeito |
|---|---|
@JsonProperty("user_name") | Mapeia um campo para uma chave JSON diferente |
@JsonIgnore | Omite um campo em ambas as direções |
@JsonInclude(NON_NULL) | Remove campos null da saída |
@JsonCreator / @JsonFormat | Construção personalizada / formatação de datas |
record Account(
@JsonProperty("user_name") String userName,
@JsonIgnore String passwordHash) {}Um exemplo prático: serializar, analisar e vincular manualmente
Jackson não está no classpath deste executor, então o programa abaixo constrói os mesmos conceitos — um escritor JSON, um parser de descida recursiva em uma árvore Map/List, e vinculação em um record — usando apenas java.util. É exatamente o que o ObjectMapper faz nos bastidores: percorre um grafo de objetos para emitir texto e tokeniza o texto para reconstruir uma árvore.
O que extrair da execução:
- A serialização percorre um grafo de objetos e emite texto. A linha
serializedmostra umMap/Listaninhado renderizado como{"name":"Ada",...,"languages":["Java","Ada"]}— um array dentro de um objeto.ObjectMapper.writeValueAsStringfaz a mesma percurso recursivo pelos getters do seu POJO ou pelos componentes de um record. - O parsing reconstrói uma árvore genérica primeiro. O tipo em tempo de execução do valor analisado é
LinkedHashMap, nãoUser— exatamente o modelo de árvore do Jackson, ondereadTreeentrega umJsonNodeque você navega por chave antes que qualquer classe seja envolvida. - Números JSON se tornam números Java, com uma decisão de tipo. O campo
agevoltou comoInteger(42) porque o texto não tinha ponto decimal; o parser escolheuIntegerem vez deDouble. Jackson faz a escolha idêntica, razão pela qual um campointse vincula de forma limpa enquanto3.14seria interpretado comoDouble. - O acesso ao campo é por nome e a ordem é preservada. Usar
LinkedHashMapmanteve as chaves na ordem de inserção, entãonameé lido antes deage. O Jackson preserva a ordem das chaves do objeto da mesma forma, razão pela qual o JSON após uma ida e volta parece com o original. - A ida e volta é sem perdas quando o modelo corresponde. A linha final re-serializou a árvore analisada para o mesmo JSON de origem, e o record
Usertipado vinculouname,ageelanguagescorretamente — o ponto central do data binding: texto entra, objeto sai, texto volta, sem desvios.