RESUM — Jackson (JSON i XML)

1) Introducció

  • Serialització: Java → text (JSON/XML). mapper.writeValueAsString(obj)
  • Deserialització: text (JSON/XML) → Java. mapper.readValue(text, Classe.class)
  • JSON: ObjectMapper · XML: XmlMapper
// JSON
ObjectMapper json = new ObjectMapper();
// XML
XmlMapper xml = new XmlMapper();

Cal afegir la configuracio recomanada (indentació, dates, ignorar camps desconeguts).

  • Mateix POJO val per a JSON i XML; en XML afegeixes només anotacions quan cal.

2) POJOs

  • Constructor buit (obligatori).
  • Getters/Setters públics (per a booleans: isActiu() és correcte).
  • Camps privats (noms en camelCase).
  • Si uses dates (LocalDate, LocalDateTime), registra JavaTimeModule.

3) Mètodes de mapper

ObjectMapper (JSON)

  • Escriure

    • writeValueAsString(obj)String
    • writeValue(File, obj) → fitxer
    • writerWithDefaultPrettyPrinter() → indenta
  • Llegir

    • readValue(String/File, Classe.class) → POJO
    • readTree(...)JsonNode (quan no tens classe)
  • Config útil

    • enable(SerializationFeature.INDENT_OUTPUT)
    • disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    • .registerModule(new JavaTimeModule())
    • disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)

XmlMapper (XML)

  • Escriure

    • writeValueAsString(obj), writeValue(File, obj), writerWithDefaultPrettyPrinter()
  • Llegir

    • readValue(String/File, Classe.class)
  • Config extra

    • configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true) (capçalera <?xml...?>)
    • Anotacions XML (@JacksonXml...) per canviar elements/atributs/wrappers


4) Anotacions JSON (ús típic)

  • @JsonProperty("nom_json") → canvia el nom de la clau
  • @JsonIgnore → oculta un camp (no serialitza/deserialitza)
  • @JsonInclude(Include.NON_NULL/NON_EMPTY/NON_DEFAULT) → omet nuls/buits/per defecte
  • @JsonFormat(pattern="dd-MM-yyyy", timezone="...") → format de dates/nombres
  • @JsonIgnoreProperties(ignoreUnknown = true) → ignora camps que venen “de més” al JSON
  • @JsonGetter("clau") / @JsonSetter("clau") → clau personalitzada sense canviar el nom del mètode
  • (Més avançat) @JsonAnySetter/@JsonAnyGetter (map de propietats dinàmiques), @JsonSerialize/@JsonDeserialize (custom)

5) Anotacions XML (ús típic)

  • @JacksonXmlRootElement(localName="arrel") → nom de l’element arrel
  • @JacksonXmlProperty(localName="nom", isAttribute=true|false) → element o atribut
  • @JacksonXmlElementWrapper(localName="contenidor", useWrapping=true) → embolcall de llistes
  • @JacksonXmlText → valor com a text intern (sense subelement)
  • @JacksonXmlCData → escriure contingut com a CDATA

Exemple mini (atribut + element):

@JacksonXmlRootElement(localName="animal")
class Animal {
  @JacksonXmlProperty(localName="tipus", isAttribute=true) String tipusAnimal; // atribut
  @JacksonXmlProperty(localName="nom") String nomComu;                         // subelement
}

XML

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

6) Llistes (JSON i XML)

JSON (sense anotacions)

class Cataleg {
  String nom;
  List<Producte> productes;
}

JSON

{"nom":"Perifèrics","productes":[{"nom":"Ratolí","preu":19.9},{"nom":"Teclat","preu":59.9}]}

XML (amb wrapper recomanat)

class Cataleg {
  String nom;
  @JacksonXmlElementWrapper(localName="productes")
  @JacksonXmlProperty(localName="producte")
  List<Producte> productes;
}

XML

<Cataleg>
  <nom>Perifèrics</nom>
  <productes>
    <producte><nom>Ratolí</nom><preu>19.9</preu></producte>
    <producte><nom>Teclat</nom><preu>59.9</preu></producte>
  </productes>
</Cataleg>

7) Dates (Java Time)

  • Afegeix jackson-datatype-jsr310 i registra el JavaTimeModule.
  • Desactiva timestamps si vols “ISO” o patró personalitzat.
@JsonFormat(pattern="dd-MM-yyyy")
LocalDate inauguracio;

8) Bones pràctiques i errors típics

  • Constructor buit als POJOs: sense ell, Jackson no pot crear l’objecte.
  • Getters/Setters coherents (especialment booleans amb isXxx).
  • Rutes de fitxer: usa src/main/resources per a dades; en proves, comprova exists().
  • Dates: JavaTimeModule + WRITE_DATES_AS_TIMESTAMPS desactivat.
  • Col·leccions: per a XML, quasi sempre necessitaràs @JacksonXmlElementWrapper + @JacksonXmlProperty.
  • Estructures desconegudes: si no tens POJOs, JsonNode és el teu amic.
  • Pretty-print: INDENT_OUTPUT o writerWithDefaultPrettyPrinter() per a llegibilitat.

Exemple Complet JSON + XML

XML

XML d’entrada:

<persona>
    <nom>Jaume Aragó</nom>
    <edat>68</edat>
    <adreces>
        <adreca tipus="casa">
            <carrer>Avinguda Datileres</carrer>
            <ciutat>Benizahat</ciutat>
        </adreca>
        <adreca tipus="oficina">
            <carrer>Canto del Bobet</carrer>
            <ciutat>Benigaslo</ciutat>
        </adreca>
    </adreces>
    <notes><![CDATA[Esta és una persona molt important.]]></notes>
</persona>

POJOs + Anotacions XML (per a mapejar exactament):

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

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

    @JacksonXmlProperty(localName = "edat")
    private int edat;

    @JacksonXmlElementWrapper(localName = "adreces") // <adreces> ... </adreces>
    @JacksonXmlProperty(localName = "adreca")        // elements fills <adreca>
    private List<Adreca> adreces;

    @JacksonXmlCData // Volem que vaja dins CDATA
    private String notes;

    public Persona() {}
    // getters/setters...
}

// Adreca.java
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

public class Adreca {
    @JacksonXmlProperty(isAttribute = true, localName = "tipus") // atribut <adreca tipus="...">
    private String tipus;

    @JacksonXmlProperty(localName = "carrer")
    private String carrer;

    @JacksonXmlProperty(localName = "ciutat")
    private String ciutat;

    public Adreca() {}
    // getters/setters...
}

Deserialitzar l’XML i mostrar-ho:

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

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

        // 1) Des de String (útil per a proves o exemples curts)
        String xmlContent = """
            <persona>
              <nom>Jaume Aragó</nom>
              <edat>68</edat>
              <adreces>
                <adreca tipus="casa">
                  <carrer>Avinguda Datileres</carrer>
                  <ciutat>Benizahat</ciutat>
                </adreca>
                <adreca tipus="oficina">
                  <carrer>Canto del Bobet</carrer>
                  <ciutat>Benigaslo</ciutat>
                </adreca>
              </adreces>
              <notes><![CDATA[Esta és una persona molt important.]]></notes>
            </persona>
        """;
        Persona p = xml.readValue(xmlContent, Persona.class);
        System.out.println("Nom: " + p.getNom() + " | Edat: " + p.getEdat());
        for (Adreca a : p.getAdreces()) {
            System.out.println(" - " + a.getTipus() + " → " + a.getCarrer() + " (" + a.getCiutat() + ")");
        }
        System.out.println("Notes: " + p.getNotes());

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

Serialitzar a XML amb capçalera i indentació:

// EscriuXML.java
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.File;
import java.util.List;

public class EscriuXML {
    public static void main(String[] args) throws Exception {
        Persona p = new Persona();
        p.setNom("Jaume Aragó");
        p.setEdat(68);
        p.setAdreces(List.of(
            crea("casa", "Avinguda Datileres", "Benizahat"),
            crea("oficina", "Canto del Bobet", "Benigaslo")
        ));
        p.setNotes("Aquesta és una persona molt important.");

        XmlMapper xml = new XmlMapper();
        xml.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true); // <?xml ...?>
        xml.enable(SerializationFeature.INDENT_OUTPUT);

        String out = xml.writeValueAsString(p);
        System.out.println(out);

        xml.writeValue(new File("src/main/resources/persona_out.xml"), p);
    }

    private static Adreca crea(String tipus, String carrer, String ciutat) {
        Adreca a = new Adreca();
        a.setTipus(tipus);
        a.setCarrer(carrer);
        a.setCiutat(ciutat);
        return a;
    }
}

JSON

POJOs sense anotacions (nom per defecte = nom del camp):

// Empresa.java
import java.util.List;

public class Empresa {
    private String nom;
    private List<Empleat> empleats;

    public Empresa() {}
    public Empresa(String nom, List<Empleat> empleats) {
        this.nom = nom;
        this.empleats = empleats;
    }
    public String getNom() { return nom; }
    public void setNom(String nom) { this.nom = nom; }
    public List<Empleat> getEmpleats() { return empleats; }
    public void setEmpleats(List<Empleat> empleats) { this.empleats = empleats; }
}

public class Empleat {
    private String nom;
    private int edat;

    public Empleat() {}
    public Empleat(String nom, int edat) { this.nom = nom; this.edat = edat; }
    public String getNom() { return nom; }
    public void setNom(String nom) { this.nom = nom; }
    public int getEdat() { return edat; }
    public void setEdat(int edat) { this.edat = edat; }
}

Serialitzar a JSON amb indentació:

// EscriuJSON.java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.File;
import java.util.List;

public class EscriuJSON {
    public static void main(String[] args) throws Exception {
        Empresa empresa = new Empresa(
            "La mazmorra del Androide",
            List.of(new Empleat("Jaume", 30), new Empleat("Bel", 28))
        );

        ObjectMapper om = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);

        System.out.println(om.writeValueAsString(empresa)); // a consola
        om.writeValue(new File("src/main/resources/empresa.json"), empresa); // a fitxer
    }
}

Deserialitzar JSON des de fitxer:

// LligJSON.java
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;

public class LligJSON {
    public static void main(String[] args) throws Exception {
        File f = new File("src/main/resources/empresa.json");
        if (!f.exists()) {
            System.err.println("No trobe empresa.json. Executa abans EscriuJSON.");
            return;
        }
        ObjectMapper om = new ObjectMapper();
        Empresa emp = om.readValue(f, Empresa.class);

        System.out.println("Empresa: " + emp.getNom());
        for (Empleat e : emp.getEmpleats()) {
            System.out.println(" - " + e.getNom() + " (" + e.getEdat() + ")");
        }
    }
}