Jackson JSON: differenza tra le annotations @JsonIgnore e @JsonIgnoreProperties

Jackson mette a disposizione due diverse annotazioni da utilizzare quando si vogliono escludere dai processi di serializzazione e deserializzazione JSON alcuni membri delle classi. Queste due annotazioni sono @JsonIgnore e @JsonIgnoreProperties.
@JsonIgnoreProperties è un’annotazione a livello di classe e prevede che vengano esplicitate le proprietà da escludere sotto forma di una di un elenco di stringhe.
@JsonIgnore invece è un’annotazione a livello di membro o metodo, che prevede che le proprietà da escludere vengano annotate puntualmente. Per escludere completamente un membro dal processo di serializzazione e deserializzazione si può annotare la proprietà, oppure il suo setter, oppure il suo getter (questo comportamento è entrato in vigore a partire dalla versione 1.9 di Jackson). E’ anche possibile escludere le proprietà in modo asimettrico tra serializzazione e deserializzazione, ma questo caso lo vediamo in un altro post.

Vediamo ora un esempio concreto di utilizzo di queste due annotations.
Creiamo una classe MyTestClass con alcune proprietà ed i relativi getters e setters ed una semplice classe di test in cui creiamo un oggetto di tale classe e poi effettuaiamo la sua serializzazione e deserializzazione JSON. Iniziamo con eseguire le operazioni sulla classe cosi com’è, senza annotazioni che escludano determinate proprietà dal processo.

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

class MyTestClass {

    private long id;
    private String name;
    private String notInterstingMember;
    private int anotherMember;
    private int forgetThisField;

    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNotInterstingMember() {
        return this.notInterstingMember;
    }

    public void setNotInterstingMember(String notInterstingMember) {
        this.notInterstingMember = notInterstingMember;
    }

    public int getAnotherMember() {
        return this.anotherMember;
    }

    public void setAnotherMember(int anotherMember) {
        this.anotherMember = anotherMember;
    }

    public int getForgetThisField() {
        return this.forgetThisField;
    }

    public void setForgetThisField(int forgetThisField) {
        this.forgetThisField = forgetThisField;
    }

    @Override
    public String toString() {
        return "MyTestClass [" + this.id + " , " +  this.name + ", " + this.notInterstingMember + ", " + this.anotherMember + ", " + this.forgetThisField + "]";
    }

}

public class JSONIgnorePropTest {

    public static void main(String[] args) {

        ObjectMapper mapper = new ObjectMapper();

        MyTestClass mtc = new MyTestClass();
        mtc.setId(1);
        mtc.setName("Test program");
        mtc.setNotInterstingMember("Don't care about this");
        mtc.setAnotherMember(100);
        mtc.setForgetThisField(-1);


        String s = null;
        try {
            s = mapper.writeValueAsString(mtc);
        }
        catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println(s);

        MyTestClass mtc2 = null;
        try {
            mtc2 = mapper.readValue(s, MyTestClass.class);
        }
        catch (JsonParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (JsonMappingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println(mtc2.toString());

    }
}

Il risultato di questa prima esecuzione è il seguente, in cui tutti i campi con i rispettivi valori vengono serializzati e poi “ricostruiti” in fase di deserializzazione.

{"id":1,"name":"Test program","notInterstingMember":"Don't care about this","anotherMember":100,"forgetThisField":-1}
MyTestClass [1 , Test program, Don't care about this, 100, -1]

Adesso supponiamo di volere escludere dal risultato delle operazioni di serializzazione e deserializzazine alcune delle proprietà della classe MyTestClass, ad esempio le proprietà “notInterstingMember” e “forgetThisField”. Vediamo, come primo caso, come ottenere questo risultato con l’annotation @JsonIgnoreProperties . Per farlo dobbiamo modificare la nostra classe come segue:

@JsonIgnoreProperties({"notInterstingMember", "forgetThisField"})
class MyTestClass {

    private long id;
    private String name;
    private String notInterstingMember;
    private int anotherMember;
    private int forgetThisField;

	// RESTO DEL CODICE...
}

Eseguendo nuovamente il programma questa volta otteniamo il risultato seguente.

{"id":1,"name":"Test program","anotherMember":100}
MyTestClass [1 , Test program, null, 100, 0]

Come possiamo vedere, nella stringa prodotta del processo di serializzazione non sono presenti i valori delle proprietà che abbiamo indicato di escludere tramite l’annotation. In fase di deserializzazione quindi, tali proprietà assumono il valore di default previsto dal loro tipo, quindi “null” per la proprietà di tipo String e 0 per la proprietà di tipo int.

Lo stesso risultato si può ottenere anche utilizzando l’altra annotation @JsonIgnore che, come detto, non è però un’annotation “di classe” ma “di membro” o “di metodo”. Modifichiamo ancora una volta la nostra classe di esempio come segue:

class MyTestClass {
	private long id;
	private String name;

	@JsonIgnore
	private String notInterstingMember;
	
	private int anotherMember;

	@JsonIgnore
	private int forgetThisField;

	// RESTO DEL CODICE...
}

Eseguendo il programma possiamo vedere che il risultato prodotto è lo stesso del caso precedente.

{"id":1,"name":"Test program","anotherMember":100}
MyTestClass [1 , Test program, null, 100, 0]

Come abbiamo detto all’inizio del post, per ottenere questo risultato utilizzando l’annotation @JsonIgnore è possibile metterla direttamente sul membro di istanza, oppure sul suo getter oppure sul suo setter. L’applicazione dell’annotation in uno qualsiasi di questi 3 punti, comporta l’esclusione totale della proprietà dal processo di serializzazione e deserializzazione (questo vale da Jackson 1.9 in poi, la versione utilizzata in questi esempi è la 2.4.3).
E’ possibile modificare questo comportamento e renderlo asimmetrico, cioè escludere una proprietà solo dalla deserializzazione utilizzando @JsonIgnore in combinazione con un’altra annotation @JsonProperty. (LINK!)

Nell’esempio seguente annotiamo con @JsonIgnore il getter della propretà “notInterstingMember” ed il setter della proprietà “forgetThisField” (senza annotare le proprietà stesse). Come possiamo vedere il risultato è ancora una volta uguale a quelli degli esempi precedenti.

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

class MyTestClass {

    private long id;
    private String name;
    private String notInterstingMember;
    private int anotherMember;
    private int forgetThisField;

    public long getId() {
        return this.id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @JsonIgnore
    public String getNotInterstingMember() {
        return this.notInterstingMember;
    }

    public void setNotInterstingMember(String notInterstingMember) {
        this.notInterstingMember = notInterstingMember;
    }

    public int getAnotherMember() {
        return this.anotherMember;
    }

    public void setAnotherMember(int anotherMember) {
        this.anotherMember = anotherMember;
    }

    public int getForgetThisField() {
        return this.forgetThisField;
    }

    @JsonIgnore
    public void setForgetThisField(int forgetThisField) {
        this.forgetThisField = forgetThisField;
    }

    @Override
    public String toString() {
        return "MyTestClass [" + this.id + " , " +  this.name + ", " + this.notInterstingMember + ", " + this.anotherMember + ", " + this.forgetThisField + "]";
    }

}
{"id":1,"name":"Test program","anotherMember":100}
MyTestClass [1 , Test program, null, 100, 0]

Il codice dei 3 diversi esempi descritti nel post è scaricabile qui:

2 thoughts on “Jackson JSON: differenza tra le annotations @JsonIgnore e @JsonIgnoreProperties

  1. Pingback: Jackson: utilizzare le annotations @JsonIgnore e @JsonProperty per escludere una proprietà solo dalla deserializzazione JSON | Dede Blog

  2. Pingback: Jackson JSON: usare l’annotazione JsonPropertyOrder per definire l’ordine di serializzazione delle proprietà | Dede Blog

Leave a Reply

Your email address will not be published. Required fields are marked *