2017-09-10 4 views
0

J'ai une simple énumération que j'aimerais sérialiser et désérialiser. La classe ressemble à ceci:Inclure le type enum pour sérialiser et désérialiser avec jackson

public enum TipusViatge { 
    OCI, 
    NEGOCIS, 
    FAMILIA; 

    @Override 
    public String toString() { 
     return name().toUpperCase(); 
    } 
} 

La chose est, je l'envoie via un appel reposant et du côté de réception peut recevoir tout type (il ne sait qu'il recevra l'objet). Donc Jackson devrait être capable de comprendre le type d'argument pour le désérialiser.

Est-il possible de le faire? Je pensais qu'inclure le nom de classe dans le json résultant devrait permettre à Jackson de comprendre le type, mais j'ai été incapable de le faire.

Répondre

0

J'ai travaillé sur ce problème pendant un certain temps. Vous pouvez désérialiser votre json avec Map<String, Object>. Cela fonctionne toujours; vous obtenez des types standard (votre énumération sera lue en tant que chaîne simple).

2nd Dans le cas général, vous savez toujours quel type d'objet vous avez lu. Ceci est un objet de niveau supérieur et vous pouvez le définir sur mapper Jackson: mapper.readerFor(cls).readValue(json). Dans le cas de votre énumération est une partie de cet objet cls, alors Jackson connaît le type et juste lire la valeur et analyser.

3ème Vous pouvez réellement avoir plusieurs objets pour une chaîne JSON. Je parle de l'héritage. Et vous pouvez regarder @JsonTypeInfo dans la documentation de Jackson.

4e Imaginons que vous ayez lu une source json et que vous ne sachiez pas ce que vous avez lu. Dans ce cas, vous pouvez demander à Jackson d'écrire un marqueur au début de l'objet. Tout comme vous demandez sur le nom de la classe. Je pense que cela concerne @JsonRootName. Vous pouvez regarder ici: Jackson JSON Deserialization with Root Element

Je pense qu'il est maintenant propre comment travailler avec des objets dans Jackson. Je veux dire que nous savons comment dire à Jackson quel élément nous voulons désérialiser. Maintenant, nous avons un problème: comment sérialiser json -> notre énumération.

5e ce n'est pas un problème et fonctionne hors de la boîte. Jackson utilise la méthode name() pour sérialiser l'énumération, et valueOf() pour la désérialiser. Vous pouvez regarder de plus près à EnumDeserializer à Jackson.

6e Je n'aime pas ce comportement, car il est cas-sencitive. J'ai fait face à la situation que lorsque les gens écrivent json string manuellement, l'utilisation en minuscules et ne peut pas le désérialiser. De plus, je crois, qu'écrire des constantes d'énumération directement dans le fichier json est une mauvaise pratique, car si je veux refactoriser les noms de l'énumération, tous les json existants devraient être modifiés aussi (brrr). Pour résoudre ces problèmes, je fais l'astuce suivante: 1. Implémenter l'interface EnumId avec l'implémentation par défaut de parseId(String id) en utilisant getId() pour identifier les constantes d'énumération et en utilisant le cas ignorer pour comparer. 1. J'ajoute le champ id à l'énumération 2. Ajouter getId() - pour la sérialisation 3. Ajouter parseId(String id) - pour la désérialisation 4.Ajouter un module Jackson ObjectMapper avec mon sérialiseur client (il

should use `getId()` instead of `name()`). 
if (enumId != null) { 
    generator.writeString(enumId.getId()); 
} 
  1. Et dire Jackson comment désérialiser cette ENUM. Ici, c'est la situation dificile, becuase dans différentes sources, l'utilisation Jackson hiérarchie de désérialisation différente et ajout d'un autre module à ObjectMapper avec désérialisation personnalisée (comme dans 4.) ne fonctionnera pas avec toutes les situations.Pour résoudre ce problème, j'ai découvert que nous pourrions ajouter @JsonCreator à la méthode parseId (String id) en énumération et Jackson l'utilisera dans toutes les situations

Je pense que c'est tout sur ce sujet. Je vous donne un exemple de code pour le rendre plus clair (il est préférable d'écrire une fois, puis expliquer deux fois):

public interface EnumId { 

    String name(); 

    default String getId() { 
     return name().toLowerCase(); 
    } 

    static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) { 
     T res = parseId(cls.getEnumConstants(), id, null); 

     if (res != null) { 
      return res; 
     } 

     throw new EnumConstantNotPresentException(cls, id); 
    } 

    static <T extends EnumId> T parseId(T[] values, String id, T def) { 
     for (T value : values) { 
      if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) { 
       return value; 
      } 
     } 

     return def; 
    } 

    static <T extends EnumId> T get(T value, T def) { 
     return value != null ? value : def; 
    } 
} 

public enum TipusViatge implements EnumId { 
    OCI, 
    NEGOCIS, 
    FAMILIA; 

    @JsonCreator 
    public static TipusViatge parseId(String id) { 
     return EnumId.parseId(TipusViatge.class, id); 
    } 
} 
+0

Merci pour vos commentaires @ oleg.cherednik! Dans mon cas, je ne connais pas les types que je vais recevoir. Utilisation de la carte et désérialisation en tant que chaînes fonctionne, mais comme je ne connais pas les types d'énumérations, j'ai besoin de les marquer d'une façon ou d'une autre afin que Jackon puisse les désérialiser. J'essaie votre suggestion #JsonRootName, je m'attendrais à ce que la chaîne jsonized qui en résulte inclue la classe enum, mais ce n'est pas le cas. –

+0

Ensuite, je pense à un sérialiseur personnalisé. Pour votre énumération, vous pouvez l'écrire et ajouter manuellement deux paramètres d'un. Pour deserialzier, vous pouvez lire le nom de la classe et ensuite l'utiliser pour déserialzie second paramètre qui est la constante d'énumération. Smth. comme ça. J'ai dans mon esprit, que j'ai vu l'implémentation par défaut de cette utilisation de certains paramètres ObjectMapper, mais je ne m'en souviens pas maintenant. Je regarde et écris si je la trouve. –

+0

Le voici: nouveau ObjectMapper(). Configure (SerializationFeature.WRAP_ROOT_VALUE, true) .writeValueAsString (TipusViatge.FAMILIA) –