1. Introdução ao Jackson
A troca de dados entre um aplicativo Java e o mundo externo muito frequentemente pode ser formulada como a seguinte tarefa: “Como transformar um objeto Java em JSON — e vice-versa?” Claro, você pode escrever seu próprio parser usando String.split e regex (e até se divertir sofrendo), mas em projetos reais ninguém faz isso.
No Java, há várias bibliotecas populares para trabalhar com JSON. A principal delas é o Jackson. Ela é tão popular que faz parte do Spring Boot e é usada em muitos outros frameworks e bibliotecas.
O que é o Jackson?
Jackson é uma biblioteca poderosa e flexível para serialização (converter um objeto Java em JSON) e desserialização (a operação inversa). Ela é composta por vários módulos, mas em 90 % dos casos você precisará de dois:
- jackson-core — o núcleo, um parser de baixo nível.
- jackson-databind — módulo de alto nível que consegue converter objetos Java para JSON e vice-versa.
Tudo o que você precisa saber para começar: se você vê a classe ObjectMapper — é o Jackson.
O Jackson é considerado o padrão de fato para trabalhar com JSON em Java porque combina simplicidade e potência. Para serializar ou desserializar dados, bastam literalmente algumas linhas de código, o que torna a biblioteca conveniente até para iniciantes. Ao mesmo tempo, ela não se limita ao básico: graças às anotações e a muitas configurações, é possível controlar de forma flexível como os dados serão transformados em objetos e vice-versa, seja para coleções, entidades aninhadas ou datas em formatos diferentes.
Também é importante que o Jackson seja muito rápido e econômico, algo crítico em projetos reais com grandes volumes de dados. Os mantenedores da biblioteca acompanham as novas versões do Java e as mudanças atuais no padrão JSON — o Jackson continua sendo uma escolha confiável tanto para aplicativos simples quanto para grandes sistemas corporativos.
2. Leitura de JSON (desserialização)
Vamos tentar ler uma string JSON e transformá-la em um objeto Java. Para isso, precisaremos de:
- Uma classe de dados (por exemplo, Person)
- A classe ObjectMapper do Jackson
Adicionando o Jackson
Se você usa Maven, adicione ao pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
Se for Gradle — de forma análoga:
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'
Exemplo de classe
public class Person {
public String name;
public int age;
}
Observação: Para simplificar, os campos foram deixados públicos. Mais adiante vamos discutir o trabalho com campos privados e getters/setters.
Exemplo de JSON
{
"name": "Alice",
"age": 30
}
Desserialização: transformando JSON em objeto
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws Exception {
String json = "{\"name\": \"Alice\", \"age\": 30}";
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json, Person.class);
System.out.println(person.name); // Alice
System.out.println(person.age); // 30
}
}
Aqui, o Jackson analisa a string JSON, encontra os campos que têm o mesmo nome que os campos ou getters da classe e preenche o objeto com os valores correspondentes usando readValue.
Desserialização de uma lista de objetos
Suponha que temos um array:
[
{ "name": "Bob", "age": 22 },
{ "name": "Eve", "age": 27 }
]
Desserializando para uma lista:
import com.fasterxml.jackson.core.type.TypeReference;
// ...
String json = "[{\"name\": \"Bob\", \"age\": 22}, {\"name\": \"Eve\", \"age\": 27}]";
ObjectMapper mapper = new ObjectMapper();
List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});
for (Person p : people) {
System.out.println(p.name + " (" + p.age + ")");
}
Talvez você se pergunte: por que não escrever simplesmente mapper.readValue(json, List.class)? Lembre-se de que os generics em Java sofrem type erasure durante a compilação. Por isso precisamos de TypeReference, para que o Jackson entenda que dentro da lista devem existir objetos do tipo Person.
3. Escrita de JSON (serialização)
Agora vamos realizar a operação inversa: transformar um objeto Java em uma string JSON.
Exemplo
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
String json = mapper.writeValueAsString(person);
System.out.println(json);
// {"name":"Charlie","age":40}
Serialização de uma lista de objetos
ObjectMapper mapper = new ObjectMapper();
List<Person> people = new ArrayList<>();
people.add(new Person("Anna", 25));
people.add(new Person("Dmitry", 31));
String json = mapper.writeValueAsString(people);
System.out.println(json);
// [{"name":"Anna","age":25},{"name":"Dmitry","age":31}]
Gravação em arquivo
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
mapper.writeValue(new File("person.json"), person);
// O arquivo person.json agora contém um objeto JSON
JSON legível (pretty)
Por padrão, o Jackson escreve tudo em uma única linha. Mas há uma opção mais “voltada para humanos” — pretty printing. Isso significa que a string JSON é exibida em um formato legível: com recuos, quebras de linha e formatação organizada.
Ao contrário do JSON “comum”, que geralmente é escrito em uma linha por compactação, a versão “bonita” é pensada para pessoas — para facilitar a visualização da estrutura de dados em logs, arquivos ou na tela.
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
String prettyJson = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(person);
System.out.println(prettyJson);
/*
{
"name" : "Charlie",
"age" : 40
}
*/
4. Anotações do Jackson
O Jackson oferece diversas anotações que permitem controlar o processo de serialização e desserialização. Aqui estão as mais úteis:
@JsonProperty
Permite especificar o nome do campo no JSON caso ele seja diferente do nome do campo na classe.
import com.fasterxml.jackson.annotation.JsonProperty;
public class Person {
@JsonProperty("full_name")
public String name;
public int age;
}
{"full_name": "Olga", "age": 28}
O Jackson entenderá que o campo full_name do JSON deve ser gravado no campo name do objeto.
@JsonIgnore
Se você não quiser serializar ou desserializar algum campo:
import com.fasterxml.jackson.annotation.JsonIgnore;
public class Person {
public String name;
@JsonIgnore
public int age;
}
No JSON não haverá o campo age, mesmo que ele exista no objeto.
@JsonInclude
Controla quais campos irão para o JSON. Por exemplo, serializar apenas os campos não nulos:
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
public String name;
public Integer age;
}
Se age == null, o JSON não terá a chave "age".
@JsonFormat
Permite definir um formato para a serialização de datas e horas.
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class Event {
public String title;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
public Date date;
}
Event event = new Event();
event.title = "Hackathon";
event.date = new Date();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(event);
// {"title":"Hackathon","date":"2024-06-07 15:23:00"}
Exemplo: tudo junto
import com.fasterxml.jackson.annotation.*;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
@JsonProperty("full_name")
private String name;
private int age;
@JsonIgnore
private String password;
// Getters e setters são obrigatórios para campos privados!
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
5. Prática: serialização e desserialização considerando as anotações
Vamos expandir seu aplicativo de estudo — agora temos a classe User com campos privados, data de registro e uma senha que não deve ir para o JSON.
import com.fasterxml.jackson.annotation.*;
import java.util.Date;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
@JsonProperty("login")
private String username;
private int age;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date registered;
@JsonIgnore
private String password;
// Getters e setters são obrigatórios!
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Date getRegistered() { return registered; }
public void setRegistered(Date registered) { this.registered = registered; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
Serialização
import com.fasterxml.jackson.databind.ObjectMapper;
import java.text.SimpleDateFormat;
ObjectMapper mapper = new ObjectMapper();
User user = new User();
user.setUsername("superuser");
user.setAge(42);
user.setRegistered(new SimpleDateFormat("yyyy-MM-dd").parse("2024-06-07"));
user.setPassword("qwerty123"); // Não irá para o JSON!
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
System.out.println(json);
/*
{
"login" : "superuser",
"age" : 42,
"registered" : "2024-06-07"
}
*/
Desserialização
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = "{ \"login\": \"superuser\", \"age\": 42, \"registered\": \"2024-06-07\" }";
User user = mapper.readValue(json, User.class);
System.out.println(user.getUsername()); // superuser
System.out.println(user.getAge()); // 42
System.out.println(user.getRegistered());// Fri Jun 07 00:00:00 ...
System.out.println(user.getPassword()); // null (e isso é bom!)
6. Erros comuns ao trabalhar com o Jackson
Erro nº 1: Ausência de construtor sem parâmetros.
O Jackson não conseguirá criar um objeto da classe se ela não tiver um construtor sem parâmetros. Isso é comum em classes onde apenas um construtor com argumentos foi declarado explicitamente.
Erro nº 2: Campos privados sem getters/setters.
Se você deixou todos os campos privados, mas esqueceu de adicionar getters e setters, o Jackson não conseguirá preenchê-los durante a desserialização (por padrão).
Erro nº 3: Nomes de campos não correspondentes.
Quando o nome do campo no JSON difere do nome do campo/getter em Java, o Jackson não encontrará a correspondência. Use @JsonProperty.
Erro nº 4: Formato de data incorreto.
Se o formato da data no JSON não corresponder ao formato esperado em Java, o Jackson emitirá um erro de parsing. Use @JsonFormat para configurar.
Erro nº 5: Tentar serializar campos com a anotação @JsonIgnore.
Esses campos não irão para o JSON — não é bug, é uma feature.
Erro nº 6: Serializar/desserializar coleções sem especificar o tipo.
Se você não usar TypeReference, o Jackson não entenderá de que tipo são os objetos dentro da coleção.
Erro nº 7: Exceção ao ler/gravar arquivo.
Não se esqueça de tratar IOException ao trabalhar com arquivos.
GO TO FULL VERSION