CodeGym /Cursos /JAVA 25 SELF /Jackson — leitura e escrita de JSON, anotações

Jackson — leitura e escrita de JSON, anotações

JAVA 25 SELF
Nível 46 , Lição 1
Disponível

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.

1
Tarefa
JAVA 25 SELF, nível 46, lição 1
Bloqueado
Privacidade dos dados do usuário 🕵️‍♀️
Privacidade dos dados do usuário 🕵️‍♀️
1
Tarefa
JAVA 25 SELF, nível 46, lição 1
Bloqueado
Gerenciamento de eventos no calendário da universidade 🗓️
Gerenciamento de eventos no calendário da universidade 🗓️
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION