J'ai un problème dans mon désérialiseur personnalisé à Jackson. Je souhaite accéder au sérialiseur par défaut pour remplir l'objet dans lequel je désérialise. Après la population, je vais faire des choses personnalisées mais je veux d'abord désérialiser l'objet avec le comportement par défaut de Jackson.
C'est le code que j'ai en ce moment.
public class UserEventDeserializer extends StdDeserializer<User> {
private static final long serialVersionUID = 7923585097068641765L;
public UserEventDeserializer() {
super(User.class);
}
@Override
@Transactional
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
User deserializedUser = null;
deserializedUser = super.deserialize(jp, ctxt, new User());
// The previous line generates an exception java.lang.UnsupportedOperationException
// Because there is no implementation of the deserializer.
// I want a way to access the default spring deserializer for my User class.
// How can I do that?
//Special logic
return deserializedUser;
}
}
Ce dont j'ai besoin, c'est d'un moyen d'initialiser le désérialiseur par défaut afin que je puisse pré-remplir mon POJO avant de démarrer ma logique spéciale.
Lors de l'appel de désérialisation depuis le désérialiseur personnalisé Il semble que la méthode soit appelée à partir du contexte actuel, quelle que soit la manière dont je construis la classe de sérialiseur. À cause de l'annotation dans mon POJO. Cela provoque une exception de débordement de pile pour des raisons évidentes.
J'ai essayé d'initialiser un BeanDeserializer
mais le processus est extrêmement complexe et je n'ai pas réussi à trouver la bonne façon de le faire. J'ai également essayé de surcharger le AnnotationIntrospector
en vain, pensant que cela pourrait m'aider à ignorer l'annotation dans le DeserializerContext
. Enfin, il semble que j'aie eu un certain succès en utilisant, JsonDeserializerBuilders
bien que cela m'oblige à faire des choses magiques pour obtenir le contexte de l'application de Spring. J'apprécierais toute chose qui pourrait me conduire à une solution plus propre, par exemple comment puis-je construire un contexte de désérialisation sans lire l' JsonDeserializer
annotation.
DeserializationContext
n'est pas quelque chose que vous devez créer ou modifier; il sera fourni parObjectMapper
.AnnotationIntrospector
, de même, ne sera d'aucune utilité pour y accéder.Réponses:
Comme StaxMan l'a déjà suggéré, vous pouvez le faire en écrivant un
BeanDeserializerModifier
et en l'enregistrant viaSimpleModule
. L'exemple suivant devrait fonctionner:la source
JsonSerializer
? J'ai plusieurs sérialiseurs mais ils ont du code en commun donc je veux le générer. J'essaie d'appeler directement le sérialiseur mais le résultat n'est pas déroulé dans le résultat JSON (chaque appel du sérialiseur crée un nouvel objet)BeanSerializerModifier
,ResolvableSerializer
etContextualSerializer
les interfaces correspondants à utiliser pour la sérialisation.readTree()
mais la réponse ne le fait pas. Quel est l'avantage de cette approche par rapport à celle proposée par Derek Cochran ? Y a-t-il un moyen de faire fonctionner celareadTree()
?J'ai trouvé une réponse à ans qui est beaucoup plus lisible que la réponse acceptée.
Ce n'est vraiment pas plus facile que ça.
la source
StackOverflowError
, puisque Jackson utilisera à nouveau le même sérialiseur pourUser
...Le
DeserializationContext
a unereadValue()
méthode que vous pouvez utiliser. Cela devrait fonctionner à la fois pour le désérialiseur par défaut et pour tous les désérialiseurs personnalisés que vous avez.Assurez-vous simplement d'appeler
traverse()
auJsonNode
niveau que vous souhaitez lire pour récupérer leJsonParser
à passerreadValue()
.la source
Il existe plusieurs façons de le faire, mais le faire correctement implique un peu plus de travail. Fondamentalement, vous ne pouvez pas utiliser de sous-classification, car les informations dont les désérialiseurs par défaut ont besoin sont construites à partir de définitions de classe.
Donc, ce que vous pouvez probablement utiliser est de construire un
BeanDeserializerModifier
, enregistrez-le via l'Module
interface (useSimpleModule
). Vous devez définir / remplacermodifyDeserializer
, et pour le cas spécifique où vous souhaitez ajouter votre propre logique (où le type correspond), construisez votre propre désérialiseur, passez le désérialiseur par défaut qui vous est donné. Et puis dansdeserialize()
méthode, vous pouvez simplement déléguer l'appel, prendre le résultat Object.Sinon, si vous devez réellement créer et remplir l'objet, vous pouvez le faire et appeler la version surchargée de
deserialize()
qui prend le troisième argument; objet dans lequel désérialiser.Une autre façon de fonctionner (mais pas sûre à 100%) serait de spécifier
Converter
object (@JsonDeserialize(converter=MyConverter.class)
). Il s'agit d'une nouvelle fonctionnalité de Jackson 2.2. Dans votre cas, Converter ne convertirait pas réellement le type, mais simplifierait la modification de l'objet: mais je ne sais pas si cela vous permettrait de faire exactement ce que vous voulez, car le désérialiseur par défaut serait appelé en premier, et seulement ensuite votreConverter
.la source
BeanDeserializerModifier
est le gestionnaire de rappel qui permet cela.S'il vous est possible de déclarer une classe utilisateur supplémentaire, vous pouvez l'implémenter simplement en utilisant des annotations
la source
Dans le sens de ce que Tomáš Záluský a suggéré , dans les cas où l'utilisation
BeanDeserializerModifier
n'est pas souhaitable, vous pouvez construire vous-même un désérialiseur par défautBeanDeserializerFactory
, bien qu'une configuration supplémentaire soit nécessaire. Dans le contexte, cette solution ressemblerait à ceci:la source
Voici un oneliner utilisant ObjectMapper
Et s'il vous plaît: Il n'est vraiment pas nécessaire d'utiliser une valeur de chaîne ou autre chose. Toutes les informations nécessaires sont fournies par JsonParser, alors utilisez-le.
la source
Je n'étais pas d'accord avec l'utilisation
BeanSerializerModifier
car cela oblige à déclarer des changements de comportement dans leObjectMapper
désérialiseur central plutôt que dans le désérialiseur personnalisé lui-même et en fait, c'est une solution parallèle pour annoter la classe d'entité avecJsonSerialize
. Si vous le ressentez de la même manière, vous apprécierez peut-être ma réponse ici: https://stackoverflow.com/a/43213463/653539la source
Une solution plus simple pour moi était simplement d'ajouter un autre bean de
ObjectMapper
et de l'utiliser pour désérialiser l'objet (grâce à https://stackoverflow.com/users/1032167/varren comment) - dans mon cas, j'étais intéressé soit à désérialiser son identifiant (un int) ou l'objet entier https://stackoverflow.com/a/46618193/986160pour chaque entité qui doit passer par un désérialiseur personnalisé, nous devons le configurer dans le
ObjectMapper
bean global de l'application Spring Boot dans mon cas (par exemple pourCategory
):la source
Vous êtes voué à l'échec si vous essayez de créer votre désérialiseur personnalisé à partir de zéro.
Au lieu de cela, vous devez obtenir l'instance de désérialiseur par défaut (entièrement configurée) via une instance personnalisée
BeanDeserializerModifier
, puis transmettre cette instance à votre classe de désérialiseur personnalisée:Remarque: Cet enregistrement de module remplace l'
@JsonDeserialize
annotation, c'est-à-dire laUser
classe ouUser
champs ne doivent plus être annotés avec cette annotation.Le désérialiseur personnalisé doit alors être basé sur un
DelegatingDeserializer
afin que toutes les méthodes soient déléguées, sauf si vous fournissez une implémentation explicite:la source
L'utilisation
BeanDeserializerModifier
fonctionne bien, mais si vous avez besoin d'utiliser,JsonDeserialize
il existe un moyen de le faireAnnotationIntrospector
comme ceci:Maintenant, le mappeur copié ignorera votre désérialiseur personnalisé (MyDeserializer.class) et utilisera l'implémentation par défaut. Vous pouvez l'utiliser dans la
deserialize
méthode de votre désérialiseur personnalisé pour éviter la récursivité en rendant le mappeur copié statique ou en le câblant si vous utilisez Spring.la source