web-dev-qa-db-fra.com

Jackson: Que se passe-t-il si une propriété est manquante?

Que se passe-t-il si j'annote un paramètre de constructeur à l'aide de @JsonProperty mais que Json ne spécifie pas cette propriété? Quelle est la valeur du constructeur?

Comment faire la différence entre une propriété ayant une valeur null et une propriété qui n'est pas présente dans le JSON?

55
Gili

Résumer les excellentes réponses de Programmeur Bruce et StaxMan :

  1. Les propriétés manquantes référencées par le constructeur se voient attribuer une valeur par défaut telle que définie par Java .

  2. Vous pouvez utiliser les méthodes setter pour différencier les propriétés définies implicitement ou explicitement. Les méthodes Setter ne sont appelées que pour les propriétés avec des valeurs explicites. Les méthodes Setter peuvent savoir si une propriété a été explicitement définie à l'aide d'un indicateur booléen (par exemple, isValueSet).

43
Gili

Que se passe-t-il si j'annote un paramètre de constructeur à l'aide de @JsonProperty mais que Json ne spécifie pas cette propriété? Quelle est la valeur du constructeur?

Pour des questions comme celle-ci, j'aime simplement écrire un exemple de programme et voir ce qui se passe.

Voici un exemple de programme.

import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;

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

    // {"name":"Fred","id":42}
    String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
    Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
    System.out.println(bar1);
    // output: 
    // Bar: name=Fred, id=42

    // {"name":"James"}
    String jsonInput2 = "{\"name\":\"James\"}";
    Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
    System.out.println(bar2);
    // output:
    // Bar: name=James, id=0

    // {"id":7}
    String jsonInput3 = "{\"id\":7}";
    Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
    System.out.println(bar3);
    // output:
    // Bar: name=null, id=7
  }
}

class Bar
{
  private String name = "BLANK";
  private int id = -1;

  Bar(@JsonProperty("name") String n, @JsonProperty("id") int i)
  {
    name = n;
    id = i;
  }

  @Override
  public String toString()
  {
    return String.format("Bar: name=%s, id=%d", name, id);
  }
}

Le résultat est que la valeur par défaut du type de données est transmise au constructeur.

Comment faire la différence entre une propriété ayant une valeur null et une propriété qui n'est pas présente dans le JSON?

Une approche simple consiste à rechercher une valeur par défaut après le traitement de la désérialisation, car si l'élément était présent dans le JSON mais avait une valeur null, cette valeur serait utilisée pour remplacer toute valeur par défaut en fonction du champ Java correspondant. Par exemple:

import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;

public class JacksonFooToo
{
  public static void main(String[] args) throws Exception
  {
    ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);

    // {"name":null,"id":99}
    String jsonInput1 = "{\"name\":null,\"id\":99}";
    BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
    System.out.println(barToo1);
    // output:
    // BarToo: name=null, id=99

    // {"id":99}
    String jsonInput2 = "{\"id\":99}";
    BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
    System.out.println(barToo2);
    // output:
    // BarToo: name=BLANK, id=99

    // Interrogate barToo1 and barToo2 for 
    // the current value of the name field.
    // If it's null, then it was null in the JSON.
    // If it's BLANK, then it was missing in the JSON.
  }
}

class BarToo
{
  String name = "BLANK";
  int id = -1;

  @Override
  public String toString()
  {
    return String.format("BarToo: name=%s, id=%d", name, id);
  }
}

Une autre approche serait d'implémenter un désérialiseur personnalisé qui recherche les éléments JSON requis. Une autre approche consisterait à consigner une demande d’amélioration avec le projet Jackson à l’adresse http://jira.codehaus.org/browse/JACKSON

34
Programmer Bruce

Outre le comportement du constructeur expliqué dans la réponse de @ Programmer_Bruce, un moyen de différencier valeur nulle et valeur manquante consiste à définir un séparateur: l'appelant n'est appelé qu'avec une valeur explicite nulle. isValueSet "ou autre) si vous voulez garder une trace des valeurs définies.

Les setters ont priorité sur les champs, dans le cas où ils existent, ce qui vous permet de "remplacer" le comportement de cette façon également.

11
StaxMan

Je pense utiliser quelque chose dans le style d'une classe Option, où un objet Nothing me dirait s'il existe une telle valeur ou non. Est-ce que quelqu'un a fait quelque chose comme ça avec Jackson (en Java, pas Scala, et autres)?

3
Roberto

(Ma réponse pourrait être utile à certaines personnes qui trouvent ce fil via Google, même s'il ne répond pas à la question des PO)
Si vous utilisez des types primitifs omissibles et que vous ne souhaitez pas utiliser un séparateur comme décrit dans les autres réponses (par exemple, si vous souhaitez que votre champ soit définitif), vous pouvez utiliser des objets boîte:

public class Foo {
    private final int number;
    public Foo(@JsonProperty Integer number) {
        if (number == null) {
            this.number = 42; // some default value
        } else {
            this.number = number;
        }
    }
}

cela ne fonctionne pas si le JSON contient réellement null, mais cela peut être suffisant si vous savez qu'il ne contiendra que des primitives ou sera absent.

0
Felk

une autre option consiste à valider l'objet après la désérialisation, manuellement ou via des frameworks tels que la validation de beans Java ou, si vous utilisez Spring, le support de validation Spring.

0
abdel