Jackson es una biblioteca popular para serializar/deserializar objetos Java en varios formatos de texto. La clase ObjectMapper es la forma principal de la biblioteca para trabajar con el formato JSON. Para otros formatos, tenemos sus descendientes ( XmlMapper , YAMLMapper ). Gracias a la herencia, podemos trabajar con todos los formatos de manera consistente, a través de una única interfaz.

Descargar archivos jar

Antes de estudiar los ejemplos, necesitamos descargar los archivos jar de Jackson y conectarlos al proyecto en IntelliJ IDEA. Tomemos el ejemplo de jackson-databind para ver cómo buscar los archivos necesarios:

  1. Vaya al sitio web del repositorio de Maven .

  2. Ingrese " jackson-databind " en el cuadro de búsqueda. Obtendrás lo siguiente:

  3. El primer resultado de la búsqueda es lo que nos interesa. Siga el enlace.

  4. A veces, se puede requerir una versión particular de una biblioteca para garantizar la compatibilidad con otros componentes en un proyecto. La última versión debería funcionar (al momento de escribir esta lección, es 2.13.2.2). Sigue el link.

  5. En la página que se abre, desea el enlace "paquete":

  6. Descargue el archivo jar usando este enlace .

Siguiendo un procedimiento similar, puede encontrar y descargar el resto de los archivos jar necesarios:

Después de descargar todos los archivos necesarios, conéctelos al proyecto en IntelliJ IDEA:

  1. Abra la configuración del proyecto (puede hacerlo con la combinación de teclas Ctrl+Alt+Shift+S ).

  2. Ir a Bibliotecas .

  3. Presiona + y luego "Java". Seleccione todos los archivos descargados. Esto es lo que deberíamos terminar con:

  4. Con esto concluye nuestro trabajo preparatorio. Ahora podemos probar ObjectMapper en acción.

Serialización a JSON

Primero, serialicemos algún objeto en JSON:


import com.fasterxml.jackson.databind.ObjectMapper;
 
class Book {
	public String title;
	public String author;
	public int pages;
}
 
public class Solution {
	public static void main(String[] args) throws Exception {
    	Book book = new Book();
    	book.title = "Good Omens";
    	book.author = "Pratchett T., Gaiman N.";
    	book.pages = 383;
 
    	ObjectMapper mapper = new ObjectMapper();
    	String jsonBook = mapper.writeValueAsString(book);
    	System.out.println(jsonBook);
	}
}

Ejecutar main le dará esta salida:

{"title":"Good Omens","author":"Pratchett T., Gaiman N.","pages":383}

El ObjectMapper tiene muchas configuraciones avanzadas. Usemos uno de ellos para hacer que la cadena JSON sea más legible. Después de crear el ObjectMapper objeto, ejecute esta sentencia:

mapper.enable(SerializationFeature.INDENT_OUTPUT);

La información en la salida sigue siendo la misma, pero ahora hay sangría y saltos de línea:

{
  "título": "Good Omens",
  "autor": "Pratchett T., Gaiman N.",
 "páginas": 383
}

Deserialización desde JSON

Ahora hagamos la acción opuesta: deserializaremos una cadena en un objeto. Para poder ver lo que está haciendo el programa, anulemos el método toString en la clase Book :


@Override
public String toString() {
	return "Book{" +
        	"title='" + title + '\'' +
        	", author='" + author + '\'' +
        	", pages=" + pages +
        	'}';
}

Y haremos lo siguiente en el método principal :


public static void main(String[] args) throws Exception {
	String jsonString = "{\"title\":\"Good Omens\",\"author\":\"Pratchett T., Gaiman N.\",\"pages\":383}";
	Book book = new ObjectMapper().readValue(jsonString, Book.class);
	System.out.println(book);
}

Producción:

Libro{title='Good Omens', author='Pratchett T., Gaiman N.', pages=383}

El método readValue está sobrecargado: tiene muchas variaciones que toman un archivo, un enlace, varios flujos de entrada, etc. Para simplificar, nuestro ejemplo usa una variante que acepta una cadena JSON.

Como se mencionó anteriormente, ObjectMapper tiene muchas configuraciones. Veamos algunos de ellos.

Ignorar propiedades desconocidas

Considere una situación en la que una cadena JSON tiene una propiedad que no existe en la clase Libro :


public static void main(String[] args) throws Exception {
	String jsonString = """
        	{
          	"title" : "Good Omens",
          	"author" : "Pratchett T., Gaiman N.",
          	"pages" : 383,
          	"unknown property" : 42
        	}""";
	ObjectMapper mapper = new ObjectMapper();
	Book book = mapper.readValue(jsonString, Book.class);
	System.out.println(book);
}

Ejecutar este código nos da una UnrecognizedPropertyException . Este es el comportamiento predeterminado, pero podemos cambiarlo:


ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Al crear un ObjectMapper objeto, usamos el método configure para establecer la configuración correspondiente en false . El método de configuración modifica el objeto al que se llamó y luego devuelve ese mismo objeto, por lo que podemos hacer esta llamada de una manera diferente:


ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

En términos de funcionalidad, esta notación es como la anterior.

Si ahora ejecutamos el método principal , la deserialización tendrá éxito y se ignorará la propiedad desconocida .

Anotaciones convenientes

Jackson nos proporciona varias anotaciones que nos permiten personalizar el proceso de serialización de muchas formas. Veamos algunos de los más útiles:

@JsonIgnore : esta anotación se coloca sobre un elemento que debe ignorarse durante la serialización/deserialización:


class Book {
	public String title;
	@JsonIgnore
	public String author;
	public int pages;
}

Aquí el autor El campo no se incluirá en el JSON resultante durante la serialización. Tras la deserialización, el autor el campo obtendrá el valor predeterminado (nulo), incluso si el JSON tenía un valor diferente.

@JsonFormat : esta anotación le permite establecer el formato de los datos serializados. Agreguemos un campo más, una Fecha , a la clase Libro :


class Book {
	public String title;
	public String author;
	public int pages;
	public Date createdDate = new Date();
}

Después de la serialización, obtenemos el siguiente JSON:

 {
  "título": "Good Omens",
  "autor": "Pratchett T., Gaiman N.",
  "páginas": 383,
  "fecha de creación": 1649330880788
}

Como puede ver, la fecha se serializó como un número. Agregaremos una anotación y estableceremos el formato:


class Book {
	public String title;
	public String author;
	public int pages;
	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
	public Date createdDate = new Date();
}

Ahora el resultado de la serialización es:

{
  "título": "Good Omens",
  "autor": "Pratchett T., Gaiman N.",
  "páginas": 383,
  "fecha de creación": "2022-04-07"
}

@JsonProperty : esta anotación le permite cambiar el nombre de la propiedad que representa el campo serializado. También puede marcar métodos con esta anotación. Si lo hace, su valor de retorno se convertirá en una propiedad JSON durante la serialización:


class Book {
	@JsonProperty("name")
	public String title;
	public String author;
	public int pages;
 
	@JsonProperty("quotedTitle")
	public String getQuotedTitle() {
    	    return "\"" + title + "\"";
	}
}

Resultado de la serialización:

{
  "autor": "Pratchett T., Gaiman N.",
  "páginas": 383,
  "nombre": "Good Omens",
  "quotedTitle": "\"Good Omens\""
}

@JsonInclude : con esta anotación, puede especificar las condiciones que se deben cumplir para que un campo se serialice. Puede aplicarlo a campos individuales o a una clase completa. Primero, intentemos serializar un objeto con campos no inicializados:


public class Solution {
	public static void main(String[] args) throws Exception {
    		Book book = new Book();

    		ObjectMapper mapper = new ObjectMapper();
    		mapper.enable(SerializationFeature.INDENT_OUTPUT);
    		String jsonBook = mapper.writeValueAsString(book);
    		System.out.println(jsonBook);
	}
}

Resultado de la serialización:

{
  "título": nulo,
  "autor": nulo,
  "páginas": 0
}

Y si agregas la anotación:


@JsonInclude(JsonInclude.Include.NON_NULL)
class Book {
	public String title;
	public String author;
	public int pages;
}

Entonces obtenemos este resultado:

{
  "páginas": 0
}

Ahora los campos que son nulos no se serializan.

@JsonPropertyOrder : esta anotación le permite establecer el orden en que se serializan los campos:


@JsonPropertyOrder({"author", "title", "pages"})
class Book {
	public String title;
	public String author;
	public int pages;
}

Resultado de la serialización:

{
  "autor": "Pratchett T., Gaiman N.",
  "título": "Good Omens",
  "páginas": 383
}

Por ahora, recuerda cómo usar las anotaciones. Al final de este módulo, los conoceremos mejor e incluso crearemos nuestras propias anotaciones.

Serialización y deserialización en XML

Si necesitamos serializar en XML, podemos usar las mismas configuraciones y anotaciones. La única diferencia será la implementación de la ObjectMapper:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new XmlMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String xmlBook = mapper.writeValueAsString(book);
	System.out.println(xmlBook);
}

Producción:

 <Libro>
  <title>Good Omens</title>
  <autor>Pratchett T., Gaiman N.</autor>
  <páginas>383</páginas>
</Libro>

Deserialización de XML:


public static void main(String[] args) throws Exception {
   String xmlString = """
            <Book>
             <title>Good Omens</title>
             <author>Pratchett T., Gaiman N.</author>
             <pages>383</pages>
           </Book>""";
   ObjectMapper mapper = new XmlMapper();
   Book book = mapper.readValue(xmlString, Book.class);
   System.out.println(book);
}

Serialización y deserialización en YAML

Manejamos YAML de la misma manera que manejamos XML:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new YAMLMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String yamlBook = mapper.writeValueAsString(book);
	System.out.println(yamlBook);
}

Producción:

---
título: "Good Omens"
autor: "Pratchett T., Gaiman N."
páginas: 383

Deserialización de YAML:


public static void main(String[] args) throws Exception {
   String yamlString = """
           ---
           title: "Good Omens"
           author: "Pratchett T., Gaiman N."
           pages: 383""";
   ObjectMapper mapper = new YAMLMapper();
   Book book = mapper.readValue(yamlString, Book.class);
   System.out.println(book);
}