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:
- JSON →
ObjectMapper
(mòdul jackson-databind) - XML →
XmlMapper
(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, semprenew XmlMapper()
.
Configuració bàsica (dates, pretty-print, etc.)
- Indentació:
mapper.enable(SerializationFeature.INDENT_OUTPUT)
omapper.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. AmbINDENT_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
perXmlMapper
icataleg.json
percataleg.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) oInclude.NON_DEFAULT
(omiteix valors per defecte) iInclude.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
iadmin
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 inom
en subelement. Si no posàremisAttribute = 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.