Mapejant amb Jackson

(Serialització i Deserialització JSON i XML)

Recorda que:

Procés Descripció Exemple
Serialització Convertir un objecte Java en JSON/XML mapper.writeValueAsString(obj)
Deserialització Convertir JSON/XML en un objecte Java mapper.readValue(json, Classe.class)

Per a mapejar dades estructurades (JSON o XML) a classes Java (POJOs/DTOs). Jackson ens ofereix un nucli comú d’anotacions i configuració que serveix tant per a JSON com per a XML, i un conjunt reduït d’anotacions específiques d’XML per a casos concrets (atributs, wrapper de llistes, etc.)

Per tant, es poden reutilitzar les mateixes classes per als dos formats i només afegir ajustos d’XML quan calga.


Serialització (Java → JSON/XML) amb Jackson

L’objectiu és convertir objectes Java (POJOs/DTOs) a text: JSON o XML. La peça clau és:

  • JSONObjectMapper (mòdul jackson-databind)
  • XMLXmlMapper (mòdul jackson-dataformat-xml)

Mètodes essencials (i què retornen)

No memoritzem, entenguem: “write” escriu (serialitza), “read” llig (deserialitza). Les versions “AsString” retornen una String; les que no, escriuen a fitxer o flux.

Per a JSON (ObjectMapper)

Retorn Mètode Què fa
String writeValueAsString(Object value) Converteix l’objecte a JSON en una String.
void writeValue(File file, Object value) Escriu l’objecte com a JSON dins d’un fitxer.
void writeValue(OutputStream out, Object value) Escriu el JSON cap a un Stream (ex: FileOutputStream).
ObjectWriter writerWithDefaultPrettyPrinter() Prepara un escriptor que indenta/formateta l’eixida. (S’usa amb .writeValueAsString(...) o .writeValue(...).)

Per a XML (XmlMapper)

És el mateix patró, però amb XML:

Retorn Mètode Què fa
String writeValueAsString(Object value) Converteix l’objecte a XML en una String.
void writeValue(File file, Object value) Escriu l’objecte com a XML dins d’un fitxer.
void writeValue(OutputStream out, Object value) Escriu l’XML cap a un Stream.

Truc didàctic: per a JSON, sempre new ObjectMapper(). Per a XML, sempre new XmlMapper().

Configuració bàsica (dates, pretty-print, etc.)

  • Indentació: mapper.enable(SerializationFeature.INDENT_OUTPUT) o mapper.writerWithDefaultPrettyPrinter()...
  • Dates modernes (Java Time API): registra JavaTimeModule i desactiva timestamps.
// JSON
ObjectMapper json = new ObjectMapper()
        .registerModule(new JavaTimeModule()) // LocalDate, LocalDateTime, ...
        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // 2025-10-13 en lloc de número
        .enable(SerializationFeature.INDENT_OUTPUT); // format identat

// XML
XmlMapper xml = (XmlMapper) new XmlMapper()
        .registerModule(new JavaTimeModule())
        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        .enable(SerializationFeature.INDENT_OUTPUT);

Per què? Sense JavaTimeModule, LocalDate dona problemes o ix com a timestamp. Amb INDENT_OUTPUT, l’eixida és llegible per a classe i exàmens.

Exemple complet JSON i XML

Partim de la classe java senzilla (o POJO):

// Producte.java
// POJO sense anotacions. Constructors buits i getters/setters perquè Jackson puga accedir.
public class Producte {
    private String nom;
    private double preu;
    private boolean actiu;

    public Producte() {} // Obligatori per a Jackson (constructor per defecte)

    public Producte(String nom, double preu, boolean actiu) {
        this.nom = nom;
        this.preu = preu;
        this.actiu = actiu;
    }

    public String getNom() { return nom; }
    public void setNom(String nom) { this.nom = nom; }

    public double getPreu() { return preu; }
    public void setPreu(double preu) { this.preu = preu; }

    public boolean isActiu() { return actiu; } // per a booleans, isXxx és OK
    public void setActiu(boolean actiu) { this.actiu = actiu; }
}

Serialitzar a JSON (String i Fitxer)

En aquest exemple, serialitzem un objecte Producte a JSON, primer com a String i després a un fitxer.

// SerialitzaJSON.java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.File;

public class SerialitzaJSON {
    public static void main(String[] args) throws Exception {
        // 1) Creem dades Java
        Producte p = new Producte("Ratolí Gamer", 24.99, true);

        // 2) Configurem l'ObjectMapper
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        // 3) Obtenir JSON com a String (per a mostrar per consola/log)
        String json = mapper.writeValueAsString(p); // <-- No toca disc, només genera una String
        System.out.println("JSON en String:");
        System.out.println(json);

        // 4) Escriure JSON a fitxer (src/main/resources és bona ubicació per a dades)
        File out = new File("src/main/resources/producte.json");
        mapper.writeValue(out, p); // <-- Ara sí, a disc
        System.out.println("S'ha guardat a: " + out.getAbsolutePath());
    }
}

L’eixida per consola serà:

{
  "nom" : "Ratolí Gamer",
  "preu" : 24.99,
  "actiu" : true
}

Serialitzar a XML (String i Fitxer)

// SerialitzaXML.java
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import java.io.File;

public class SerialitzaXML {
    public static void main(String[] args) throws Exception {
        Producte p = new Producte("Ratolí Gamer", 24.99, true);

        XmlMapper xml = new XmlMapper();
        xml.registerModule(new JavaTimeModule());
        xml.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        xml.enable(SerializationFeature.INDENT_OUTPUT);
        // Opcional: afegir la capçalera XML <?xml ...?>
        xml.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);

        String xmlString = xml.writeValueAsString(p);
        System.out.println("XML en String:");
        System.out.println(xmlString);

        File out = new File("src/main/resources/producte.xml");
        xml.writeValue(out, p);
        System.out.println("S'ha guardat a: " + out.getAbsolutePath());
    }
}

L’eixida per consola serà:

<?xml version='1.0' encoding='UTF-8'?>
<Producte>
  <nom>Ratolí Gamer</nom>
  <preu>24.99</preu>
  <actiu>true</actiu>
</Producte>

Nota XML: Com que no hem posat anotacions d’XML, Jackson deriva noms d’etiqueta dels noms de camp. Si després vols <nom_producte> en lloc de <nom>, tindrem que posar anotacions XML.

Serialitzar llistes i objectes anidats

// Cataleg.java
import java.util.List;

public class Cataleg {
    private String nom;
    private List<Producte> productes;

    public Cataleg() {}
    public Cataleg(String nom, List<Producte> productes) {
        this.nom = nom;
        this.productes = productes;
    }
    public String getNom() { return nom; }
    public void setNom(String nom) { this.nom = nom; }
    public List<Producte> getProductes() { return productes; }
    public void setProductes(List<Producte> productes) { this.productes = productes; }
}
// SerialitzaColleccions.java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.util.List;
import java.io.File;

public class SerialitzaColleccions {
    public static void main(String[] args) throws Exception {
        Cataleg cat = new Cataleg(
            "Perifèrics",
            List.of(
                new Producte("Ratolí Gamer", 24.99, true),
                new Producte("Teclat Mecànic", 59.90, true)
            )
        );

        ObjectMapper om = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
        // A String
        System.out.println(om.writeValueAsString(cat));
        // A fitxer
        om.writeValue(new File("src/main/resources/cataleg.json"), cat);
    }
}

El resultat JSON serà:

{
  "nom" : "Perifèrics",
  "productes" : [ {
    "nom" : "Ratolí Gamer",
    "preu" : 24.99,
    "actiu" : true
  }, {
    "nom" : "Teclat Mecànic",
    "preu" : 59.9,
    "actiu" : true
  } ]
}

La versió amb XmlMapper és idèntica, només canviant la classe i el nom del fitxer.

  • Es a dir, canviem ObjectMapper per XmlMapper i cataleg.json per cataleg.xml.

i l’eixida serà:

<?xml version='1.0' encoding='UTF-8'?>
<Cataleg>
  <nom>Perifèrics</nom>
  <productes>
    <productes>
      <nom>Ratolí Gamer</nom>
      <preu>24.99</preu>
      <actiu>true</actiu>
    </productes>
    <productes>
      <nom>Teclat Mecànic</nom>
      <preu>59.9</preu>
      <actiu>true</actiu>
    </productes>
  </productes>
</Cataleg>

Deserialització (JSON/XML → Java)

Ara fem el camí contrari: a partir d’un text (JSON o XML) o un fitxer, Jackson crea objectes Java.

Mètodes de lectura

  • JSON (ObjectMapper)
Retorn Mètode Què fa
<T> readValue(String content, Class<T> valueType) Converteix una String JSON a un objecte Java.
<T> readValue(File src, Class<T> valueType) Converteix un fitxer JSON a un objecte Java.
JsonNode readTree(String/Reader/File/InputStream) Llig el JSON com a arbre (quan no tens POJO o vols inspeccionar).
  • XML (XmlMapper)

Exactament igual, però amb XmlMapper.

Deserialitzar JSON (String i Fitxer)

En este exemple partime del string JSON i d’un fitxer producte.json:

String json = """ 
{ "nom": "Raton Gamer", "preu": 24.99, "actiu": true } 
""";

El fitxer `producte.json` hauria de tenir el format:

```json
{
  "nom": "Raton Gamer",
  "preu": 24.99,
  "actiu": true
}
// DeserialitzaJSON.java
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;

public class DeserialitzaJSON {
    public static void main(String[] args) throws Exception {
        ObjectMapper om = new ObjectMapper();

        // 1) Des de String
        String json = """
          { "nom": "Raton Gamer", "preu": 24.99, "actiu": true }
        """;
        Producte p = om.readValue(json, Producte.class);
        System.out.println("Nom: " + p.getNom() + ", preu: " + p.getPreu());

        // 2) Des de Fitxer
        File f = new File("src/main/resources/producte.json");
        if (f.exists()) {
            Producte p2 = om.readValue(f, Producte.class);
            System.out.println("Des de fitxer → " + p2.getNom());
        } else {
            System.err.println("No trobe el fitxer producte.json");
        }
    }
}

Deserialitzar XML (String i Fitxer)

// DeserialitzaXML.java
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.File;

public class DeserialitzaXML {
    public static void main(String[] args) throws Exception {
        XmlMapper xml = new XmlMapper();

        String xmlString = """
          <Producte>
            <nom>Raton Gamer</nom>
            <preu>24.99</preu>
            <actiu>true</actiu>
          </Producte>
        """;

        Producte p = xml.readValue(xmlString, Producte.class);
        System.out.println("Nom: " + p.getNom() + ", actiu: " + p.isActiu());

        File f = new File("src/main/resources/producte.xml");
        if (f.exists()) {
            Producte p2 = xml.readValue(f, Producte.class);
            System.out.println("Des de fitxer → " + p2.getNom());
        }
    }
}

El fitxer producte.xml hauria de tenir el format:

<Producte>
  <nom>Raton Gamer</nom>
  <preu>24.99</preu>
  <actiu>true</actiu>
</Producte>

Quan no tens POJOs o el JSON és “lliure”: JsonNode

Quan no disposem d’un model fix (POJOs) o només volem llegir camps concrets d’un JSON desconegut, podem usar l’API d’arbre de Jackson (JsonNode).

En aquest curs treballarem amb POJOs i anotacions, per tant, sols saber que existeix.

// Llegir sense classe, recorrent l'arbre
import com.fasterxml.jackson.databind.*;

public class LlegirComArbre {
    public static void main(String[] args) throws Exception {
        String json = """
          { "ciutat":"Madrid", "temperatures":[19,21,18,23,20] }
        """;
        ObjectMapper om = new ObjectMapper();
        JsonNode root = om.readTree(json); // Arrel de l’arbre

        String ciutat = root.get("ciutat").asText();
        System.out.println("Ciutat: " + ciutat);

        // Iterar l'array
        for (JsonNode t : root.withArray("temperatures")) {
            System.out.println("Temp: " + t.asInt());
        }
    }
}

Nota: La crida a root.withArray("temperatures") és una forma de navegar l’arbre JSON utilitzant Jackson, i obtindre directament un array de nodes JSON.


Anotacions en Jackson

(Personalitzant el mapeig en JSON i XML)

Quan treballem amb Jackson, per defecte el mapeig entre un objecte Java i un fitxer JSON o XML és automàtic: cada atribut privat amb el seu getter/setter genera una clau JSON o un element XML amb el mateix nom.

Tanmateix, en projectes reals sovint necessitem més control:

  • Canviar noms de claus o elements.
  • Ometre camps.
  • Convertir un camp en atribut XML.
  • Mostrar el contingut dins de CDATA.
  • Donar format a dates.

Per aconseguir-ho, utilitzem anotacions. Jackson té dos conjunts principals:

Tipus d’anotació Paquet Àmbit
JSON com.fasterxml.jackson.annotation Serialització i deserialització de JSON
XML com.fasterxml.jackson.dataformat.xml.annotation Control d’etiquetes, atributs, wrappers, CDATA…

Anotacions JSON

Este conjunt s’aplica quan treballem amb ObjectMapper i fitxers .json.

Permeten controlar com Jackson mapeja les propietats d’una classe Java (POJO) cap a les claus d’un document JSON, i viceversa.

Les utilitzarem sobretot per:

  • canviar noms de camps,
  • ocultar informació sensible,
  • ometre valors nuls o per defecte,
  • definir formats de data,
  • o ignorar propietats desconegudes en la deserialització.

@JsonProperty("nom_json")

Esta anotació canvia el nom de la clau que apareix al JSON durant la serialització o la deserialització. Es molt útil quan el nom del camp en Java no coincideix amb el nom desitjat en JSON.

Exemple:

import com.fasterxml.jackson.annotation.JsonProperty;

public class Producte {
    @JsonProperty("nom_producte") // Clau personalitzada del JSON
    private String nom;

    private double preu;

    public Producte() {}
    public Producte(String nom, double preu) {
        this.nom = nom;
        this.preu = preu;
    }

    public String getNom() { return nom; }
    public double getPreu() { return preu; }
}

Codi per a serialitzar:

ObjectMapper om = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
String json = om.writeValueAsString(new Producte("Ratolí", 19.95));
System.out.println(json);

Eixida JSON:

{
  "nom_producte": "Ratolí",
  "preu": 19.95
}

Jackson utilitzarà nom_producte en lloc de nom. Aquesta anotació també funciona a nivell de mètodes get o set.


@JsonIgnore

Evita que un camp aparega en el JSON, tant en la serialització (Java → JSON) com en la deserialització (JSON → Java).

S’utilitza, per exemple, per ocultar dades sensibles com contrasenyes o tokens.

Exemple:

import com.fasterxml.jackson.annotation.JsonIgnore;

public class Usuari {
    private String nom;
    @JsonIgnore
    private String contrasenya;

    public Usuari() {}
    public Usuari(String nom, String contrasenya) {
        this.nom = nom;
        this.contrasenya = contrasenya;
    }
    // getters/setters
}

Eixida JSON:

{
  "nom": "jaume"
}

Esta anotació també funciona a nivell de mètodes get o set.


@JsonInclude(Include.NON_NULL)

Omet camps que són null, buits o valors per defecte, esta etiqueta es útil per a reduir la mida del JSON i evitar informació innecessària, especialment en APIs i fitxers JSON grans.

Exemple:

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Alumne {
    private String nom;
    private String email; // pot ser null

    public Alumne(String nom, String email) {
        this.nom = nom;
        this.email = email;
    }
}

Serialitzar dos objectes:

ObjectMapper om = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
System.out.println(om.writeValueAsString(new Alumne("Maria", "maria@ies.com")));
System.out.println(om.writeValueAsString(new Alumne("Pere", null)));

Eixida JSON:

{
  "nom": "Maria",
  "email": "maria@ies.com"
}
{
  "nom": "Pere"
}

El segon objecte no mostra email perquè és null. També es pot usar Include.NON_EMPTY (omiteix buits) o Include.NON_DEFAULT (omiteix valors per defecte) i Include.ALWAYS (sempre inclou).


@JsonFormat(pattern="dd-MM-yyyy")

Controla el format de sortida i entrada de valors que representen dates, hores o números. Sense definir el patró, Jackson mostra els objectes LocalDate o Date com a timestamps numèrics.

Exemple:

import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDate;

public class Event {
    private String nom;

    @JsonFormat(pattern = "dd-MM-yyyy")
    private LocalDate data;

    public Event(String nom, LocalDate data) {
        this.nom = nom;
        this.data = data;
    }
}

Eixida JSON:

{
  "nom": "Concert",
  "data": "15-10-2025"
}

Sense el patró, la data apareixeria com a timestamp numèric. És recomanable registrar també el mòdul JavaTimeModule.

@JsonFormat té altres opcions útils, com shape (per a definir si és string, número, etc.) i timezone (per a zones horàries).

    @JsonFormat(
        pattern = "dd/MM/yyyy HH:mm",
        shape = JsonFormat.Shape.STRING,
        timezone = "Europe/Madrid"
    )

@JsonIgnoreProperties({"a","b"})

Ignora diverses propietats alhora, útil quan llegim un document JSON amb més camps dels que tenim a la nostra classe Java, i no volem que Jackson done error per camps desconeguts.

Exemple:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties({"id", "admin"})
public class Perfil {
    private String nom;
    private String rol;
    // getters/setters
}

Si el JSON d’entrada és:

{
  "id": 1001,
  "nom": "Pau",
  "rol": "usuari",
  "admin": true
}

Després de deserialitzar:

nom = Pau
rol = usuari

Els camps id i admin s’han ignorat, encara que existien al JSON.

Si no sabem quins camps ignorar, podem usar @JsonIgnoreProperties(ignoreUnknown = true) per a ignorar tots els camps desconeguts.

@JsonIgnoreProperties(ignoreUnknown = true)
public class Perfil {
    private String nom;
    private String rol;
}

Això evita haver d’enumerar cada camp desconegut manualment i evita errors de deserialització.


@JsonGetter i @JsonSetter

Permeten personalitzar els noms dels getters o setters sense canviar el nom real dels mètodes o camps al codi Java.

Exemple:

import com.fasterxml.jackson.annotation.JsonGetter;

public class Llibre {
    private String titol;

    @JsonGetter("nom_llibre")
    public String getTitol() {
        return titol;
    }

    public void setTitol(String titol) {
        this.titol = titol;
    }
}

Eixida JSON:

{
  "nom_llibre": "1984"
}

El nom del mètode continua sent getTitol(), però Jackson mostrarà nom_llibre al JSON.


Anotacions XML

Aquest conjunt d’anotacions s’aplica quan fem servir XmlMapper per a llegir o escriure arxius .xml. Permeten controlar com es tradueixen les classes i camps Java a elements, atributs i estructures XML.

Per utilitzar-les cal importar:

import com.fasterxml.jackson.dataformat.xml.annotation.*;

Amb aquestes anotacions podem:

  • Definir el nom de l’element arrel (@JacksonXmlRootElement)
  • Canviar el nom d’un element o fer que siga atribut (@JacksonXmlProperty)
  • Agrupar llistes dins d’un contenidor (@JacksonXmlElementWrapper)
  • Escriure text intern o CDATA (@JacksonXmlText, @JacksonXmlCData)

@JacksonXmlRootElement(localName="arrel")

Defineix el nom de l’element arrel del document XML. Per defecte, Jackson utilitza el nom de la classe (Persona, Producte, etc.), però amb aquesta anotació podem personalitzar-lo.

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

@JacksonXmlRootElement(localName = "persona")
public class Persona {
    private String nom;
    private int edat;

    public Persona() {}
    public Persona(String nom, int edat) {
        this.nom = nom;
        this.edat = edat;
    }

    public String getNom() { return nom; }
    public int getEdat() { return edat; }
}

Eixida XML:

<persona>
  <nom>Jaume</nom>
  <edat>30</edat>
</persona>

Si no especifiquem localName, l’element arrel s’anomenarà igual que la classe (<Persona>).

Arguments principals:

  • localName: nom de l’element arrel del document.

@JacksonXmlProperty(localName="nom") i isAttribute=true

Serveix per canviar el nom d’un element XML o indicar que ha de ser un atribut dins de l’etiqueta principal.

  • Si no s’indica res, cada camp es converteix automàticament en un subelement.
  • Si s’indica isAttribute = true, apareixerà com a atribut XML dins de la mateixa etiqueta.

Exemple:

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

public class Animal {
    // A l'XML vull l'atribut "tipus", però en Java el camp es diu "tipusAnimal"
    @JacksonXmlProperty(isAttribute = true, localName = "tipus")
    private String tipusAnimal;

    // A l'XML vull l'element <nom>, però en Java el camp es diu "nomComu"
    @JacksonXmlProperty(localName = "nom")
    private String nomComu;

    public Animal() {}

    public Animal(String tipusAnimal, String nomComu) {
        this.tipusAnimal = tipusAnimal;
        this.nomComu = nomComu;
    }

    public String getTipusAnimal() { return tipusAnimal; }
    public void setTipusAnimal(String tipusAnimal) { this.tipusAnimal = tipusAnimal; }

    public String getNomComu() { return nomComu; }
    public void setNomComu(String nomComu) { this.nomComu = nomComu; }
}

Eixida XML:

<Animal tipus="mamífer">
  <nom>Lleó</nom>
</Animal>

En aquest cas, tipus s’ha convertit en atribut i nom en subelement. Si no posàrem isAttribute = true, el resultat seria <tipus>mamífer</tipus>.

Arguments principals:

  • localName: nom personalitzat per a l’element o atribut.
  • isAttribute: indica si el camp ha de ser un atribut (true) o un element (false per defecte).

@JacksonXmlElementWrapper + @JacksonXmlProperty

S’utilitzen conjuntament per a gestionar col·leccions o llistes (List, Set, etc.) dins d’un document XML.

  • @JacksonXmlElementWrapper crea un contenidor per a tots els elements.
  • @JacksonXmlProperty indica el nom de cada element intern dins de la llista.

Exemple:

import com.fasterxml.jackson.dataformat.xml.annotation.*;
import java.util.List;

@JacksonXmlRootElement(localName = "zoologic")
public class Zoologic {
    @JacksonXmlElementWrapper(localName = "animals") // Contenidor de la llista
    @JacksonXmlProperty(localName = "animal")        // Nom de cada element dins
    private List<Animal> animals;

    public Zoologic() {}
    public Zoologic(List<Animal> animals) {
        this.animals = animals;
    }

    public List<Animal> getAnimals() { return animals; }
}

Eixida XML:

<zoologic>
  <animals>
    <animal><nom>Lleó</nom></animal>
    <animal><nom>Girafa</nom></animal>
  </animals>
</zoologic>

Sense @JacksonXmlElementWrapper, els elements <animal> apareixerien directament un darrere d’un altre, sense contenidor <animals>.

Arguments principals:

  • localName: nom del contenidor (per a @JacksonXmlElementWrapper) o del camp (per a @JacksonXmlProperty).
  • useWrapping: true (per defecte) crea el contenidor; false el suprimeix.

@JacksonXmlCData

Indica que el contingut del camp s’ha d’envoltar dins d’una secció CDATA. Això evita que Jackson escape caràcters especials (<, >, &), molt útil en textos llargs o fragments HTML.

Exemple:

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;

public class Noticia {
    private String titular;

    @JacksonXmlCData // Preserva el contingut sense escapament
    private String contingut;

    public Noticia() {}
    public Noticia(String titular, String contingut) {
        this.titular = titular;
        this.contingut = contingut;
    }

    public String getTitular() { return titular; }
    public String getContingut() { return contingut; }
}

Eixida XML:

<Noticia>
  <titular>Última hora</titular>
  <contingut><![CDATA[L'API ha fallat <500> errors múltiples]]></contingut>
</Noticia>

Els caràcters especials dins <![CDATA[...]]> no es processen com XML, es mantenen intactes.

Arguments principals:

  • No té arguments. Aplica directament a un camp String.

@JacksonXmlText

Assigna el valor directament com a text intern de l’etiqueta, en lloc de generar un subelement. S’utilitza quan un element XML conté únicament text i no més etiquetes internes.

Exemple:

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;

public class Valor {
    @JacksonXmlText // El text anirà directament dins l’etiqueta
    private String contingut;

    public Valor() {}
    public Valor(String contingut) {
        this.contingut = contingut;
    }

    public String getContingut() { return contingut; }
}

Eixida XML:

<Valor>Text sense subetiquetes</Valor>

Aquesta anotació és molt pràctica per a textos curts, valors numèrics o missatges senzills.


Resum anotacions

JSON

Anotació Funcionalitat / Ús principal Arguments més habituals
@JsonProperty("nom_json") Canvia el nom de la clau al JSON. Permet usar noms diferents entre la classe Java i el document JSON. value: nou nom de la propietat.
@JsonIgnore Exclou un camp de la serialització i/o deserialització. Ideal per a dades sensibles (com contrasenyes).
@JsonInclude(JsonInclude.Include.NON_NULL) Omet camps nuls, buits o amb valor per defecte per reduir la mida del JSON. value: NON_NULL, NON_EMPTY, NON_DEFAULT, etc.
@JsonFormat(pattern="dd-MM-yyyy") Defineix el format d’un valor, especialment dates (LocalDate, Date) o nombres. pattern, shape, timezone
@JsonIgnoreProperties({"a","b"}) Ignora diverses propietats alhora. Evita errors en deserialitzar JSONs amb camps desconeguts. value: camps a ignorar, ignoreUnknown=true
@JsonGetter("nom") Personalitza el nom que es mostrarà al JSON per un getter. value: nom de la clau al JSON.
@JsonSetter("nom") Personalitza el nom esperat al llegir un JSON (deserialització). value: nom de la clau al JSON.
@JsonAnySetter / @JsonAnyGetter Assigna o recupera propietats desconegudes a un Map. Útil per JSONs flexibles.
@JsonAutoDetect Controla quins camps o mètodes són visibles per a Jackson (encara que no tinguen getters/setters). fieldVisibility, getterVisibility, etc.
@JsonSerialize / @JsonDeserialize Permet definir una classe pròpia per a (de)serialitzar camps amb lògica personalitzada. using: classe del (de)serialitzador personalitzat.

XML

Anotació Funcionalitat / Ús principal Arguments més habituals
@JacksonXmlRootElement(localName="arrel") Defineix el nom de l’element arrel del document XML (equivalent al nom de la classe). localName: nom de l’arrel.
@JacksonXmlProperty(localName="nom", isAttribute=true) Canvia el nom d’un element o el converteix en atribut XML. localName: nom nou. isAttribute: true si és atribut.
@JacksonXmlElementWrapper(localName="contenidor") Agrupa una llista o col·lecció dins d’un contenidor. Evita que apareguen elements sense agrupació. localName: nom del contenidor. useWrapping: activa o desactiva l’embolcall.
@JacksonXmlCData Indica que el contingut s’ha d’envoltar amb CDATA, preservant caràcters especials (<, >, &, etc.).
@JacksonXmlText Escriu el valor del camp com a text intern de l’etiqueta, no com a subelement.

Recorda: En XML, les anotacions serveixen per controlar la jerarquia i aparença exacta del document:

  • @JacksonXmlRootElement → controla el nom de l’arrel.
  • @JacksonXmlProperty → controla subelements o atributs.
  • @JacksonXmlElementWrapper → controla agrupacions (<llista> ... </llista>).
  • @JacksonXmlCData i @JacksonXmlText → controlen el contingut intern.

Les anotacions de Jackson per a JSON controlen sobretot noms, visibilitat i formats, mentre que les d’XML controlen estructura, jerarquia i atributs. Les dos comparteixen la mateixa filosofia: mapejar classes Java a dades estructurades d’una manera precisa i llegible.