JSON - Mapejant amb Jackson
El procés per serialitzar i deserialitzar documents JSON amb Java és similar al que s’utilitza per mapejar XML amb Jackson.
Abans d’utilitzar Jackson hem de dissenyar les classes JAVA per a reflectir l’estructura del JSON amb el estem treballant. Per tant, en general seguirem els següents passos:
-
Afegir Dependències: Utilitzarem Maven/Gradle per afegir les dependències Jackson al nostre projecte, i poder utilitzar les funcions que ens proporciona Jackson.
-
Analitzar l’estructura JSON: Identificant els objectes, les propietats i els valors que conté. Això ens donarà una idea clara de com dissenyar les classes Java.
-
Crear Classes Java: Crearem una classe Java per a cada objecte o element complex del JSON. Per exemple, si el JSON té un objecte anomenat “estudiant” amb propietats com “nom,” “edat” i “notes,” crearem una classe Java anomenada “Estudiant” amb aquestes propietats.
-
Mapejar les propietats: Posarem Anotacions Jackson en les classes Java, per indicar com serialitzarem/deserialitzarem les dades JSON. (@JsonProperty, @JsonIgnore, @JsonFormat, entre d’altres.)
-
Estructures de Dades: Si el document JSON conté estructures de dades com ara arrays, també hem de dissenyar classes per a aquestes estructures.
-
Recorda:
- Afegir els mètodes getters i setters per a les propietats de les classes Java per a que Jackson puga accedir i modificar les propietats.
- Podem afegir constructors, mètodes addicionals i altres mètodes de lògica de negoci segons les necessitats del teu projecte.
Una volta dissenyades les classe JAVA ja podríem parsejar amb Jackson utilitzant:
-
Objecte ObjectMapper: L’ObjectMapper de Jackson és el nucli de la llibreria i s’utilitza per a realitzar les operacions de serialització i deserialització. Aquest objecte proporciona mètodes per la conversió entre objectes Java i JSON. (Recorda que la classe XmlMapper deriva de ObjectMapper).
-
Gestió d’excepcions: En la deserialització, és important gestionar possibles excepcions com ara errors de format JSON o problemes d’accés a les propietats de l’objecte. Jackson proporciona maneig d’excepcions i estratègies per controlar aquestes situacions. També possibles errors d’escriptura en la serialització.
ObjectMapper
ObjectMapper és la classe central en la biblioteca Jackson que es fa servir per a convertir dades entre formats de Java i JSON. Principalment usarem writeValueAsString
, writeValue
i readValue
, que poden estar sobrecarregats.
Tipus | Mètode | Descripció |
---|---|---|
String | writeValueAsString(Object value) | Converteix un objecte Java a una cadena JSON. |
void | writeValue(File outputFile, Object value) | Escriu l’objecte Java a un fitxer JSON. |
void | writeValue(OutputStream out, Object value) | Escriu l’objecte Java a un flux (Stream) d’arxiu de sortida com JSON. |
<T> | readValue(String content, Class<T> valueType) | Deserialitza una cadena JSON a un objecte Java de la classe especificada. |
<T> | readValue(File src, Class<T> valueType) | Deserialitza un fitxer JSON a un objecte Java. |
<T> | readValue(Reader src, Class<T> valueType) | Deserialitza un contingut JSON des d’un objecte Reader a un objecte Java. |
<T> | readValue(InputStream src, Class<T> valueType) | Deserialitza un flux d’entrada (input stream) que conté JSON a un objecte Java. |
JsonNode | readTree(JsonParser jsonParser) | Llegeix un arbre JSON (JsonNode) a partir d’un objecte JsonParser. |
Exemples:
- Deserialització de JSON en un objecte Java:
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{\"nom\": \"Jaume\", \"edat\": 33}";
Persona persona = objectMapper.readValue(jsonString, Persona.class);
Utilitzem readValue()
per a deserialitzar una cadena JSON en un objecte Java de la classe Persona.
- Serialització d’un objecte Java com a JSON:
ObjectMapper objectMapper = new ObjectMapper();
Persona persona = new Persona("Jaume", 33);
String jsonString = objectMapper.writeValueAsString(persona);
writeValueAsString()
serialitza un objecte Persona com a una cadena JSON.
- Lectura (Deserialització) de fitxers JSON a objectes Java:
ObjectMapper objectMapper = new ObjectMapper();
File jsonFile = new File("persona.json");
Persona persona = objectMapper.readValue(jsonFile, Persona.class);
readValue()
deserialitza les dades d’un fitxer JSON en un objecte Java de la classe Persona.
- Escriptura (Serialització) d’objectes Java a un fitxer JSON:
ObjectMapper objectMapper = new ObjectMapper();
Persona persona = new Persona("Jaume", 33);
objectMapper.writeValue(new File("persona.json"), persona);
Anotacions JSON amb Jackson
Jackson proporciona diverses anotacions que podem utilitzar per a personalitzar la serialització i deserialització de JSON. EStes anotacions ens permeten controlar com es mapegen les propietats de les classes Java a JSON i viceversa.
1. @JsonProperty
L’anotació @JsonProperty
permet canviar el nom d’un camp d’un objecte Java quan es converteix en JSON o quan es llegeix JSON per crear un objecte Java.
import com.fasterxml.jackson.annotation.JsonProperty;
public class Persona {
@JsonProperty("nom_complet")
private String nom;
@JsonProperty("edat_persona")
private int edat;
// Constructors, getters i setters
}
Serialització
Quan serialitzem un objecte Java a JSON, @JsonProperty
modifica els noms de les claus JSON d’acord amb l’anotació.
Entrada Java:
- Nom: Jaume
- Edat: 33
Sortida JSON:
{
"nom_complet": "Jaume",
"edat_persona": 33
}
Deserialització
Durant la deserialització, Jackson utilitza els noms de les claus definits amb @JsonProperty
per mapar les dades JSON als camps de l’objecte Java corresponent.
Entrada JSON:
{
"nom_complet": "Jaume",
"edat_persona": 33
}
Objecte Java resultant:
- Objecte
Persona
:- Nom: Jaume
- Edat: 33
2. @JsonIgnore
Impedeix que un camp siga serialitzat o deserialitzat. Esta anotació és útil per excloure informació sensible (com contrasenyes) o dades que no són necessàries en la representació JSON.
Exemple
import com.fasterxml.jackson.annotation.JsonIgnore;
public class Persona {
private String nom;
@JsonIgnore
private String password;
// Constructors, getters i setters
}
Serialització
Quan serialitzem un objecte Java a JSON, el camp marcat amb @JsonIgnore
serà exclòs del resultat.
Entrada Java:
- Nom: Jaume
- Password: secreta
Sortida JSON:
{
"nom": "Jaume"
}
Deserialització
Durant la deserialització, Jackson ignorarà les propietats marcades amb @JsonIgnore
en el JSON, i no les assignarà a l’objecte Java resultant.
Entrada JSON:
{
"nom": "Jaume",
"password": "secreta"
}
Objecte Java resultant:
- Objecte
Persona
:- Nom: Jaume
- Password: null (camp ignorat)
3. @JsonInclude
L’anotació @JsonInclude
permet controlar quins camps s’inclouen en el JSON depenent del seu valor. Es pot utilitzar per excloure camps que són null
, buits o que tenen un valor per defecte.
Aquesta anotació es pot configurar amb diferents opcions:
- no
null
:@JsonInclude(Include.NON_NULL)
- no buits :
@JsonInclude(NON_EMPTY)
- no tinguen el valor per defecte :
@JsonInclude(Include.NON_DEFAULT)
Exemple:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
public class Persona {
private String nom;
@JsonInclude(Include.NON_NULL) // Exclou si és null
private String adreca;
@JsonInclude(Include.NON_DEFAULT) // Exclou si és el valor per defecte
private int edat = 0;
@JsonInclude(Include.NON_EMPTY) // Exclou si la llista està buida
private List<String> telefons;
// Constructors, getters i setters
}
Serialització
Quan els objectes Java es serialitzen a JSON, els camps marcats amb @JsonInclude
s’inclouen només si compleixen les condicions establertes (no són null
, buits o no tenen el valor per defecte).
Entrada Java:
- Nom: Jaume
- Adreça: null
- Edat: 0
- Llista de telèfons: []
Sortida JSON:
{
"nom": "Jaume"
}
- El camp
adreca
no s’inclou perquè ésnull
. - El camp
edat
no s’inclou perquè és el valor per defecte (0
). - La llista
telefons
no s’inclou perquè està buida.
Deserialització
Durant la deserialització, Jackson assigna les propietats presents al JSON als camps corresponents. Si un camp està absent perquè és null
, buit o té el valor per defecte, l’objecte Java mantindrà els seus valors per defecte.
Entrada JSON:
{
"nom": "Jaume"
}
Objecte Java resultant:
- Objecte
Persona
:- Nom: Jaume
- Adreça: null
- Edat: 0
- Llista de telèfons: []
4. @JsonFormat
L’anotació @JsonFormat
s’utilitza per especificar com es formaten certs camps durant la serialització i deserialització. És especialment útil per a camps de dates, nombres o altres tipus de dades que necessiten un format específic.
Aquesta anotació es pot configurar amb diferents opcions, com ara:
shape
: Defineix la forma (com a cadena, nombre, etc.).pattern
: Especifica el patró de format (per exemple, per a dates o nombres).timezone
: Permet establir la zona horària per als camps de data i hora.
Exemple:
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class Persona {
private String nom;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone = "Europe/Madrid")
private Date dataNaixement;
@JsonFormat(shape = JsonFormat.Shape.STRING) // Formateja com a cadena
private double salari;
// Constructors, getters i setters
}
Serialització
Quan els objectes Java es serialitzen a JSON, l’anotació @JsonFormat
garanteix que els camps segueixen el format especificat.
Entrada Java:
- Nom: Jaume
- Data de Naixement: 15-10-1990 (amb el format
Date
de Java) - Salari: 3000.50 (com a número
double
)
Sortida JSON:
{
"nom": "Jaume",
"dataNaixement": "15-10-1990",
"salari": "3000.50"
}
El camp dataNaixement
s’inclou amb el format de data especificat i el camp salari
es formateja com una cadena, gràcies a l’anotació @JsonFormat
.
Deserialització
Durant la deserialització, Jackson llegeix els camps amb el format indicat i els assigna a l’objecte Java amb el tipus adequat.
Entrada JSON:
{
"nom": "Jaume",
"dataNaixement": "15-10-1990",
"salari": "3000.50"
}
Objecte Java resultant:
Objecte Persona
:
- Nom: Jaume
- Data de Naixement: 15 d’octubre de 1990 (objecte
Date
) - Salari: 3000.50 (com a
double
)
5. @JsonSetter
L’anotació @JsonSetter
s’utilitza per indicar quin mètode setter s’ha d’utilitzar durant la deserialització. És especialment útil quan el nom de la clau JSON no coincideix amb el nom de la propietat a la classe Java, permetent mapar els valors del JSON a mètodes específics de l’objecte Java.
Exemple:
import com.fasterxml.jackson.annotation.JsonSetter;
public class Persona {
private String nom;
private int edat;
private double salari;
@JsonSetter("nom_complet") // Mapeja la clau JSON "nom_complet" al camp "nom"
public void setNom(String nom) {
this.nom = nom;
}
@JsonSetter("edat_persona") // Mapeja la clau JSON "edat_persona" al camp "edat"
public void setEdat(int edat) {
this.edat = edat;
}
@JsonSetter("salari_personal") // Mapeja la clau JSON "salari_personal" al camp "salari"
public void setSalari(double salari) {
this.salari = salari;
}
// Getters opcionals per a mostrar valors
}
Serialització
Durant la serialització, aquesta anotació no afecta, ja que només controla la deserialització. Així, en la serialització, les claus JSON seguiran els noms originals dels camps.
Entrada Java:
- Nom: Jaume
- Edat: 33
- Salari: 3000.50
Sortida JSON:
{
"nom": "Jaume",
"edat": 33,
"salari": 3000.50
}
Deserialització
Durant la deserialització, Jackson utilitza els mètodes setter indicats per assignar els valors del JSON als camps corresponents de l’objecte Java, fins i tot quan els noms de les claus JSON són diferents.
Entrada JSON:
{
"nom_complet": "Jaume",
"edat_persona": 33,
"salari_personal": 3000.50
}
Objecte Java resultant:
Objecte Persona
:
- Nom: Jaume
- Edat: 33
- Salari: 3000.50
6. @JsonGetter
Funció: És l’equivalent de @JsonSetter
, però s’utilitza durant la serialització per indicar quin mètode getter
s’ha d’utilitzar per generar una clau JSON, això permet canviar el nom de la clau JSON quan es genera l’objecte JSON a partir d’un objecte Java, fins i tot si el nom de la propietat a Java és diferent.
Exemple:
import com.fasterxml.jackson.annotation.JsonGetter;
public class Persona {
private String nom;
private int edat;
private double salari;
@JsonGetter("nom_complet") // Canvia la clau JSON a "nom_complet"
public String getNom() {
return nom;
}
@JsonGetter("edat_persona") // Canvia la clau JSON a "edat_persona"
public int getEdat() {
return edat;
}
@JsonGetter("salari_personal") // Canvia la clau JSON a "salari_personal"
public double getSalari() {
return salari;
}
// Setters opcionals per a deserialització
}
Serialització
Quan un objecte Java es serialitza a JSON, @JsonGetter
s’utilitza per canviar els noms de les claus JSON segons el mètode getter indicat.
Entrada Java:
- Nom: Jaume
- Edat: 33
- Salari: 3000.50
Sortida JSON:
{
"nom_complet": "Jaume",
"edat_persona": 33,
"salari_personal": 3000.50
}
Deserialització
Durant la deserialització, @JsonGetter
no té efecte, ja que només afecta la serialització. Els noms de les claus JSON es deserialitzen normalment amb els noms de propietats estàndard, llevat que utilitzes altres anotacions com @JsonSetter
.
Entrada JSON:
{
"nom_complet": "Jaume",
"edat_persona": 33,
"salari_personal": 3000.50
}
Objecte Java resultant:
Objecte Persona
:
- Nom: Jaume
- Edat: 33
- Salari: 3000.50
7. @JsonValue
L’anotació @JsonValue
s’utilitza per indicar que un mètode ha de proporcionar la representació JSON completa de l’objecte Java. Quan s’utilitza aquesta anotació, el valor retornat pel mètode marcat amb @JsonValue
serà la representació JSON de l’objecte sencer, en lloc de serialitzar l’objecte amb les seves propietats habituals.
Exemple:
import com.fasterxml.jackson.annotation.JsonValue;
public class Persona {
private String nom;
private int edat;
public Persona(String nom, int edat) {
this.nom = nom;
this.edat = edat;
}
@JsonValue // Serialitza tota la classe com el valor retornat per aquest mètode
public String obtenirNomComplet() {
return nom + " (edat: " + edat + ")";
}
}
Serialització
Quan es serialitza un objecte Java a JSON, @JsonValue
fa que el mètode anotat obtenirNomComplet()
retorne el valor JSON complet, i es fa servir aquesta representació en lloc d’utilitzar els noms de les propietats individuals.
Entrada Java:
- Nom: Jaume
- Edat: 33
Sortida JSON:
"Jaume (edat: 33)"
Explicació: La serialització retorna una única cadena que conté el nom i l’edat en el format definit pel mètode obtenirNomComplet()
.
Deserialització
@JsonValue
no afecta la deserialització de l’objecte Java, ja que només controla la manera com es genera la sortida JSON durant la serialització.
Entrada JSON:
"Jaume (edat: 33)"
Objecte Java resultant:
No s’aplica deserialització directa amb @JsonValue
, ja que aquest mètode només defineix com es serialitza l’objecte, però no com es deserialitza.
8. @JsonAnySetter i @JsonAnyGetter
Les anotacions @JsonAnySetter
i @JsonAnyGetter
es fan servir per gestionar propietats dinàmiques que no estan predefinides com a camps dins d’una classe Java. Aquestes anotacions permeten treballar amb claus JSON que no es coneixen anticipadament i emmagatzemar-les en un Map
dins de l’objecte Java.
@JsonAnySetter
: Assigna propietats dinàmiques durant la deserialització.@JsonAnyGetter
: Serialitza propietats dinàmiques durant la serialització.
Exemple:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import java.util.Map;
import java.util.HashMap;
public class Persona {
private String nom;
private Map<String, Object> propietatsExtra = new HashMap<>();
public Persona(String nom) {
this.nom = nom;
}
@JsonAnySetter // Assigna propietats addicionals durant la deserialització
public void afegirPropietat(String clau, Object valor) {
propietatsExtra.put(clau, valor);
}
@JsonAnyGetter // Serialitza les propietats addicionals durant la serialització
public Map<String, Object> obtenirPropietatsExtra() {
return propietatsExtra;
}
// Getters i setters opcionals per al camp "nom"
}
Serialització
Durant la serialització, @JsonAnyGetter
permet afegir al JSON totes les propietats dinàmiques que no estan explícitament definides en la classe Java, utilitzant un Map
.
Entrada Java:
- Nom: Jaume
- Propietats extra: ciutat = València, telèfon = 123456789
Sortida JSON:
{
"nom": "Jaume",
"ciutat": "València",
"telefon": "123456789"
}
Deserialització
Durant la deserialització, @JsonAnySetter
assigna qualsevol propietat addicional que no es mapege directament amb els camps de la classe a un Map
.
Entrada JSON:
{
"nom": "Jaume",
"ciutat": "València",
"telefon": "123456789"
}
Objecte Java resultant:
Objecte Persona
:
- Nom: Jaume
- Propietats Extra:
- Ciutat: València
- Telèfon: 123456789
9. @JsonIgnoreProperties
L’anotació @JsonIgnoreProperties
permet ignorar un conjunt de propietats durant la serialització i la deserialització, sense haver d’utilitzar @JsonIgnore
en cada camp individualment. Això és útil quan es vol evitar processar diverses propietats d’una sola vegada, especialment si no es volen incloure en el resultat JSON o no són rellevants.
Exemple:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties({"adreca", "telefon"}) // Ignora aquests camps durant la serialització i deserialització
public class Persona {
private String nom;
private String adreca;
private String telefon;
private int edat;
// Constructors, getters i setters
}
Serialització
Quan els objectes Java es serialitzen a JSON, les propietats especificades amb @JsonIgnoreProperties
no s’inclouen en el resultat JSON.
Entrada Java:
- Nom: Jaume
- Adreça: Carrer Falsa, 123
- Telèfon: 123456789
- Edat: 33
Sortida JSON:
{
"nom": "Jaume",
"edat": 33
}
- Els camps
adreca
itelefon
es marquen amb@JsonIgnoreProperties
i, per tant, s’exclouen de la sortida JSON.
Deserialització
Durant la deserialització, qualsevol propietat especificada amb @JsonIgnoreProperties
al JSON s’ignora, de manera que no es mapeja al camp corresponent de l’objecte Java.
Entrada JSON:
{
"nom": "Jaume",
"adreca": "Carrer Falsa, 123",
"telefon": "123456789",
"edat": 33
}
Objecte Java resultant:
Objecte Persona
:
- Nom: Jaume
- Adreça: null (ignorada)
- Telèfon: null (ignorat)
- Edat: 33
10. @JsonAutoDetect
L’anotació @JsonAutoDetect
permet controlar quins camps i mètodes Jackson ha d’inspeccionar durant la serialització i deserialització. Aquesta anotació és útil per a configurar la visibilitat dels camps i mètodes, permetent, per exemple, que Jackson accedisca a camps privats sense necessitat de mètodes getter o setter. També pots utilitzar aquesta anotació per indicar que Jackson només ha de veure camps públics o accedir a qualsevol camp, independentment de la seva visibilitat.
Opcions comunes de @JsonAutoDetect
:
fieldVisibility
: Controla la visibilitat dels camps de la classe.getterVisibility
: Controla si Jackson utilitza mètodes getter per accedir als valors dels camps.setterVisibility
: Controla si Jackson utilitza mètodes setter per establir valors als camps.isGetterVisibility
: Controla la visibilitat dels getters que comencen amb “is” (generalment per a camps booleans).creatorVisibility
: Controla la visibilitat dels constructors per a deserialitzar objectes.
Exemple:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
@JsonAutoDetect(
fieldVisibility = Visibility.ANY, // Jackson pot accedir a tots els camps, incloent els privats
getterVisibility = Visibility.NONE, // Ignora els mètodes getter
setterVisibility = Visibility.NONE // Ignora els mètodes setter
)
public class Persona {
private String nom;
private String adreca;
// No es requereixen getters ni setters, Jackson accedirà directament als camps privats
}
Serialització
Quan Jackson serialitza un objecte Java a JSON, l’anotació @JsonAutoDetect
fa que puga accedir als camps privats de la classe directament, fins i tot si no hi ha mètodes getter disponibles.
Entrada Java:
Persona persona = new Persona();
persona.nom = "Jaume";
persona.adreca = "Carrer Curt, 123";
Sortida JSON:
{
"nom": "Jaume",
"adreca": "Carrer Curt, 123"
}
- Encara que els camps
nom
iadreca
són privats i no hi ha mètodes getter, Jackson pot accedir-hi directament gràcies a l’anotació@JsonAutoDetect(fieldVisibility = Visibility.ANY)
.
Deserialització
a l’hora de deserialitzar, Jackson pot utilitzar l’anotació @JsonAutoDetect
per accedir directament als camps privats, fins i tot si no hi ha mètodes setter o constructors visibles públicament.
Entrada JSON:
{
"nom": "Jaume",
"adreca": "Carrer Curt, 123"
}
Objecte Java resultant:
Persona persona = new Persona();
persona.nom = "Jaume";
persona.adreca = "Carrer Curt, 123";
- Gràcies a
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
, Jackson pot deserialitzar directament les dades als camps privats, fins i tot sense mètodes setter.
Aquesta anotació és útil quan treballes amb classes amb camps privats als quals vols accedir directament sense haver d’exposar mètodes getter o setter.
11. @JsonDeserialize
i @JsonSerialize
Les anotacions @JsonDeserialize
i @JsonSerialize
permeten personalitzar com Jackson deserialitza (JSON a Java) i serialitza (Java a JSON) camps o objectes específics. Aquestes anotacions són útils quan necessites una lògica especial per transformar els valors en formats no estàndards, com afegir símbols, transformar valors complexos, o modificar l’estructura del JSON.
@JsonSerialize
: Defineix una classe o mètode per personalitzar la serialització d’un camp o objecte a JSON.@JsonDeserialize
: Defineix una classe o mètode per personalitzar la deserialització d’un camp o objecte des de JSON a un objecte Java.
Exemple:
Suposem que tenim un objecte Persona
amb un camp salari
que ha de ser serialitzat afegint un símbol d’euros i deserialitzat eliminant aquest símbol.
Serialitzador personalitzat:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
public class SalariSerializer extends StdSerializer<Double> {
public SalariSerializer() {
this(null);
}
public SalariSerializer(Class<Double> t) {
super(t);
}
@Override
public void serialize(Double value, JsonGenerator gen, SerializerProvider provider) throws IOException {
// Afegeix el símbol d'euros al salari
gen.writeString(value + "€");
}
}
Deserialitzador personalitzat:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
public class SalariDeserializer extends StdDeserializer<Double> {
public SalariDeserializer() {
this(null);
}
public SalariDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Double deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// Elimina el símbol d'euros i converteix el valor a Double
String salariText = p.getText().replace("€", "");
return Double.parseDouble(salariText);
}
}
Classe Persona
amb @JsonSerialize
i @JsonDeserialize
:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class Persona {
private String nom;
@JsonSerialize(using = SalariSerializer.class) // Utilitza el SalariSerializer per afegir el símbol d'euros
@JsonDeserialize(using = SalariDeserializer.class) // Utilitza el SalariDeserializer per eliminar el símbol d'euros
private Double salari;
// Constructor, getters i setters
public Persona(String nom, Double salari) {
this.nom = nom;
this.salari = salari;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public Double getSalari() {
return salari;
}
public void setSalari(Double salari) {
this.salari = salari;
}
}
Serialització
Quan Jackson serialitza l’objecte Persona
a JSON, utilitza el SalariSerializer
per afegir el símbol d’euros al camp salari
.
Entrada Java:
Persona persona = new Persona("Jaume", 3000.50);
Sortida JSON:
{
"nom": "Jaume",
"salari": "3000.5€"
}
- Durant la serialització,
@JsonSerialize
indica a Jackson que utilitze la classeSalariSerializer
per afegir el símbol d’euros al campsalari
, convertint-lo en una cadena amb el format"3000.5€"
.
Deserialització
Durant la deserialització, Jackson utilitza SalariDeserializer
per eliminar el símbol d’euros i reconvertir la cadena a un número Double
.
Entrada JSON:
{
"nom": "Jaume",
"salari": "3000.5€"
}
Sortida Java:
Persona persona = new Persona("Jaume", 3000.50);
- Durant la deserialització,
@JsonDeserialize
indica a Jackson que utilitzeSalariDeserializer
per eliminar el símbol d’euros i convertir el valor"3000.5€"
a3000.5
com aDouble
.
Resum:
@JsonSerialize
: S’utilitza per personalitzar com es serialitzen els camps o objectes a JSON. En aquest cas, elSalariSerializer
s’utilitza per afegir el símbol d’euros a la sortida JSON.@JsonDeserialize
: S’utilitza per personalitzar com es deserialitzen els camps o objectes des de JSON. En aquest cas, elSalariDeserializer
elimina el símbol d’euros i converteix el valor de text aDouble
.
Aquestes anotacions són útils quan necessites controlar el format de certs camps, afegir transformacions personalitzades o manejar formats de dades especials.
Explicació: Jackson utilitza les classes CustomSerializer
i CustomDeserializer
per gestionar la serialització i deserialització personalitzada del camp nom
.
Resum Complet d’Anotacions JSON en Jackson
Anotació | Descripció | Exemple |
---|---|---|
@JsonProperty | Canvia el nom d’un camp en el JSON. | @JsonProperty("nom_complet") |
@JsonIgnore | Evita que un camp es serialitzi o deserialitzi. | @JsonIgnore |
@JsonInclude | Inclou un camp només sota certes condicions (per exemple, si no és null). | @JsonInclude(Include.NON_NULL) |
@JsonFormat | Especifica el format per a camps com dates o nombres. | @JsonFormat(shape = JsonFormat.Shape.STRING) |
@JsonSetter | Indica quin mètode setter s’utilitza per deserialitzar JSON. | @JsonSetter("nom_complet") |
@JsonGetter | Especifica quin mètode getter s’utilitza per serialitzar JSON. | @JsonGetter("nom_complet") |
@JsonValue | Serialitza un objecte complet com un únic valor retornat per un mètode. | @JsonValue |
@JsonAnySetter i @JsonAnyGetter | Gestionen propietats dinàmiques no definides en la classe Java. | @JsonAnySetter , @JsonAnyGetter |
@JsonIgnoreProperties | Ignora un conjunt de propietats durant la serialització i deserialització. | @JsonIgnoreProperties({"adreca", "telefon"}) |
@JsonAutoDetect | Controla la visibilitat de camps i mètodes per a la serialització i deserialització. | @JsonAutoDetect(fieldVisibility = Visibility.ANY) |
@JsonDeserialize i @JsonSerialize | Permeten especificar serialitzadors i deserialitzadors personalitzats. | @JsonDeserialize(using = CustomDeserializer.class) |
Creació de POJOs Java a partir de JSON
És important que primer analitzem l’estructura del document JSON, identificant els objectes, els arrays, les propietats i els valors que conté. Això ens donarà una idea clara de com dissenyar les classes Java.
1. Objecte Simple
JSON:
{
"nom": "Jaume Aragó",
"edat": 55,
"actiu": true
}
POJO:
public class Persona {
private String nom;
private int edat;
private boolean actiu;
// Getters i setters
}
2. Array Simple
JSON:
["blau", "verd", "groc"]
POJO:
public class Colors {
private List<String> colors;
// Getters i setters
}
3. Objectes Anidats
JSON:
{
"usuari": {
"nom": "Jaume",
"edat": 91
}
}
POJO:
public class Dades {
private Usuari usuari;
// Getters i setters
}
public class Usuari {
private String nom;
private int edat;
// Getters i setters
}
4. Objecte amb Arrays
JSON:
{
"noms": ["John", "Jane", "Mike"],
"edats": [30, 28, 35]
}
POJO:
public class Dades {
private List<String> noms;
private List<Integer> edats;
// Getters i setters
}
5. JSON complex
JSON:
{
"empresa": {
"nom": "La mazmorra del Androide",
"empleats": [
{
"nom": "Jaume",
"edat": 30
},
{
"nom": "Bel",
"edat": 28
}
]
}
}
POJO:
public class Empresa {
private String nom;
private List<Empleat> empleats;
// Getters i setters
}
public class Empleat {
private String nom;
private int edat;
// Getters i setters
}
Exemple:
JSON:
{
"estudiant": {
"nom": "Armando Bronca Segura",
"edat": 20,
"matriculat": true,
"assignatures": ["Llenguatge de Marques", "Bases de Dades", "Programació Web"],
"notes": {
"llenguatge_de_marques": {
"parcial_1": 9.5,
"parcial_2": 8.7
},
"bases_de_dades": {
"parcial_1": 8.0,
"parcial_2": 9.2
},
"programacio_web": {
"parcial_1": 7.8,
"parcial_2": 8.9
}
}
},
"professor": {
"nom": "Jaume Aragó",
"especialitzacio": "Accés a Dades",
"contacte": {
"correu": "j.aragovalls@edu.gva.es",
"telefon": "+34 555 555 555"
}
},
"institut": "IES Benigasló",
"curs_academic": "1936-1937",
"comentaris": [
{
"data": "1936-10-15",
"text": "Armando ha fet bons progressos en el curs fins ara."
},
{
"data": "1936-11-30",
"text": "És important seguir treballant en les bases de dades."
}
]
}
POJO:
public class Estudiant {
private String nom;
private int edat;
private boolean matriculat;
private List<String> assignatures;
private Map<String, Notes> notes;
private String curs_academic;
private String nomInstitut;
private List<Comentari> comentaris;
// Getters i setters
}
public class Notes {
private double parcial_1;
private double parcial_2;
// Getters i setters
}
public class Professor {
private String nom;
private String especialitzacio;
private Contacte contacte;
// Getters i setters
}
public class Contacte {
private String correu;
private String telefon;
// Getters i setters
}
public class Comentari {
private String data;
private String text;
// Getters i setters
}
public class Detalls {
private String comentaris;
// Getters i setters
}
A l’estructura del JSON anterior, les “assignatures” són una llista d’elements, mentre que les “notes” estan estructurades com un mapa on les claus són noms d’assignatures i els valors són objectes de la classe “Notes” (que tenen els camps “parcial_1” i “parcial_2”).
Per a reflectir aquesta estructura en les classes Java, utilitzem:
- List per a les “assignatures” perquè és una llista d’elements sense cap clau associada.
- Map per a les “notes” perquè volem associar cada assignatura a les seves notes utilitzant el nom de l’assignatura com a clau.
Això ens permetrà accedir fàcilment a les notes d’una assignatura específica quan deserialitzem el JSON a objectes Java utilitzant Jackson, ja que podem utilitzar el nom de l’assignatura com a clau per accedir a les seves notes.
Anotacions amb Jackson - JSON
1. Objectes Anidats
JSON:
{
"usuari": {
"nom": "Jaume",
"edat": 91
}
}
POJO:
public class Dades {
@JsonProperty("usuari")
private Usuari usuari;
// Getters i setters
}
public class Usuari {
@JsonProperty("nom")
private String nom;
@JsonProperty("edat")
private int edat;
// Getters i setters
}
2. Objecte amb Arrays
JSON:
{
"noms": ["Jaume", "Isabel", "Sergi"],
"edats": [30, 28, 35]
}
POJO:
public class Dades {
@JsonProperty("noms")
private List<String> noms;
@JsonProperty("edats")
private List<Integer> edats;
// Getters i setters
}
3. JSON complex
JSON:
{
"empresa": {
"nom": "La mazmorra del Androide",
"empleats": [
{
"nom": "Jaume",
"edat": 30
},
{
"nom": "Bel",
"edat": 28
}
]
}
}
POJO:
public class Empresa {
@JsonProperty("nom")
private String nom;
@JsonProperty("empleats")
private List<Empleat> empleats;
// Getters i setters
}
public class Empleat {
@JsonProperty("nom")
private String nom;
@JsonProperty("edat")
private int edat;
// Getters i setters
}
Nota: En este exemple no se serialitzaria
"empresa": {
en el JSON de sortida, caldria crear una classe superior que creara objectes de tipus empresa:
public class Root {
@JsonProperty("empresa")
public Empresa empresa;
}
Depenent de les nostres necessitats o de les condicions del projecte podem afegir alguna de les següents anotacions:
- @JsonInclude: Si al serialitzar volem incloure nuls o buits.
- @JsonIgnore: Al Serialitzar i Deserialitzar, ignorem l’atribut.
- @JsonFormat: Per a donar format a tipus especials com Objectes DATA.
- @JsonCreator: Per personalitzar el constructor d’alguna classe.
- @JsonValue: Al Serialitzar. Indica quin mètode utilitzarem per a escriure el JSON (quin GETTER)
- @JsonSetter: Al Deserialitzar. Indica quin mètode utilitzarem per a llegir del JSON.
Exemple- Serialització JSON
public class Root {
@JsonProperty("empresa")
public Empresa empresa;
// Constructor, Getters, Setter i altres mètodes
}
class Empresa {
@JsonProperty("Nom_Empresa")
private String nom;
@JsonProperty("empleats_de_empresa")
private List<Empleat> empleats;
// Constructor, Getters, Setter i altres mètodes
}
class Empleat {
@JsonProperty("Nom_Empleat")
private String nom;
@JsonProperty("Edat_de_Empleat")
private int edat;
// Constructor, Getters, Setter i altres mètodes
}
El programa principal quedaria:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class SerialJson {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
// Identació de l’eixida a l’estil JSON
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
// Crear una llista d'empleats
Empleat empleat1 = new Empleat("Jaume", 30);
Empleat empleat2 = new Empleat("Bel", 28);
List<Empleat> empleats = new ArrayList<>();
empleats.add(empleat1);
empleats.add(empleat2);
// Crear una empresa amb la llista d'empleats
Empresa empresa = new Empresa("La mazmorra del Androide", empleats);
// Crear l'objecte principal
Root empresa_root = new Root(empresa);
// Serialitzar l'empresa a un fitxer JSON
try {
File fitxer = new File("src/main/resources/llistatEmpleats.json");
objectMapper.writeValue(fitxer, empresa_root);
System.out.println("S'ha creat el fitxer 'llistatEmpleats.json'.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Per a formatar l’eixida JSON hem de posar:
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
Exemple- Deserialització JSON
Si partim del fitxer Empleats.json
:
{
"empresa": {
"Nom_Empresa": "La mazmorra del Androide",
"empleats_de_empresa": [
{
"Nom_Empleat": "Jaume",
"Edat_de_Empleat": 30
},
{
"Nom_Empleat": "Bel",
"Edat_de_Empleat": 28
}
]
}
}
Codi Java per deserialitzar:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.List;
public class DeserialJSON {
public static void main(String[] args) {
try {
ObjectMapper objectMapper = new ObjectMapper();
File jsonFile = new File("src/main/resources/Empleats.json");
Root root_empresa = objectMapper.readValue(jsonFile, Root.class);
// Mostrar el contingut de l'empresa
Empresa empresa = root_empresa.getEmpresa();
System.out.println("Nom de la empresa: " + empresa.getNom());
// Mostrar el contingut dels empleats
List<Empleat> empleats = empresa.getEmpleats();
System.out.println("Empleats:");
for (Empleat empleat : empleats) {
System.out.println("Nom: " + empleat.getNom() + ", Edat: " + empleat.getEdat());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Eixida esperada:
Nom de la empresa: La mazmorra del Androide
Empleats:
Nom: Jaume, Edat: 30
Nom: Bel, Edat: 28
En resum: L’exemple mostra com deserialitzar un fitxer JSON a un objecte Java. El JSON inicial es llig des del fitxer Empleats.json
i es converteix en una instància de la classe Root
, que conté una instància d’Empresa
, la qual al seu torn conté una llista d’empleats. El codi recorre la llista d’empleats i mostra les dades.