Ich habe ein Problem mit meinem benutzerdefinierten Deserializer in Jackson. Ich möchte auf den Standard-Serializer zugreifen, um das Objekt zu füllen, in das ich deserialisiere. Nach der Population werde ich einige benutzerdefinierte Dinge tun, aber zuerst möchte ich das Objekt mit dem Standardverhalten von Jackson deserialisieren.
Dies ist der Code, den ich im Moment habe.
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;
}
}
Was ich brauche, ist eine Möglichkeit, den Standard-Deserializer zu initialisieren, damit ich mein POJO vorab füllen kann, bevor ich meine spezielle Logik starte.
Beim Aufrufen von Deserialize aus dem benutzerdefinierten Deserializer heraus scheint die Methode aus dem aktuellen Kontext aufgerufen zu werden, unabhängig davon, wie ich die Serializer-Klasse konstruiere. Wegen der Anmerkung in meinem POJO. Dies führt aus offensichtlichen Gründen zu einer Stapelüberlaufausnahme.
Ich habe versucht, a zu initialisieren, BeanDeserializer
aber der Prozess ist äußerst komplex und ich habe es nicht geschafft, den richtigen Weg zu finden. Ich habe auch versucht, das AnnotationIntrospector
ohne Erfolg zu überladen , weil ich dachte, es könnte mir helfen, die Anmerkung in der zu ignorieren DeserializerContext
. Schließlich scheint es, als hätte ich einige Erfolge erzielt, JsonDeserializerBuilders
obwohl ich dafür einige magische Dinge tun musste, um den Anwendungskontext von Spring zu erhalten. Ich würde mich über alles freuen, was mich zu einer saubereren Lösung führen könnte, zum Beispiel darüber, wie ich einen Deserialisierungskontext erstellen kann, ohne die JsonDeserializer
Anmerkung zu lesen .
DeserializationContext
ist nichts, was Sie entweder erstellen oder ändern sollten; es wird bereitgestellt vonObjectMapper
.AnnotationIntrospector
Ebenso wird es nicht hilfreich sein, Zugang zu erhalten.Antworten:
Wie StaxMan bereits vorgeschlagen hat, können Sie dies tun, indem Sie ein schreiben
BeanDeserializerModifier
und es über registrierenSimpleModule
. Das folgende Beispiel sollte funktionieren:quelle
JsonSerializer
? Ich habe mehrere Serializer, aber sie haben Code gemeinsam, also möchte ich ihn generieren. Ich versuche, den Serializer direkt aufzurufen, aber das Ergebnis wird nicht im JSON-ErgebnisBeanSerializerModifier
,ResolvableSerializer
undContextualSerializer
sind die passenden Schnittstellen für die Serialisierung.readTree()
, die Antwort jedoch nicht. Was ist der Vorteil dieses Ansatzes gegenüber dem von Derek Cochran ? Gibt es eine Möglichkeit, diese Arbeit zu machenreadTree()
?Ich habe bei ans eine Antwort gefunden, die viel besser lesbar ist als die akzeptierte Antwort.
Einfacher geht es wirklich nicht.
quelle
StackOverflowError
, da Jackson den gleichen Serializer wieder verwenden wird fürUser
...Das
DeserializationContext
hat einereadValue()
Methode, die Sie verwenden können. Dies sollte sowohl für den Standard-Deserializer als auch für alle benutzerdefinierten Deserializer funktionieren.Rufen Sie einfach
traverse()
dieJsonNode
Ebene an, die Sie lesen möchten, um dieJsonParser
zu übergebende Ebene abzurufenreadValue()
.quelle
Es gibt verschiedene Möglichkeiten, dies zu tun, aber um es richtig zu machen, ist etwas mehr Arbeit erforderlich. Grundsätzlich können Sie keine Unterklassifizierung verwenden, da Informationen, die Standard-Deserialisierer benötigen, aus Klassendefinitionen erstellt werden.
Was Sie also höchstwahrscheinlich verwenden können, ist das Erstellen eines
BeanDeserializerModifier
Registers über dieModule
Schnittstelle (VerwendungSimpleModule
). Sie müssen definieren / überschreibenmodifyDeserializer
und für den speziellen Fall, in dem Sie Ihre eigene Logik hinzufügen möchten (wo der Typ übereinstimmt), Ihren eigenen Deserializer erstellen und den angegebenen Standard-Deserializer übergeben. Und dann können Sie in derdeserialize()
Methode einfach den Aufruf delegieren und das Ergebnisobjekt übernehmen.Wenn Sie das Objekt tatsächlich erstellen und füllen müssen, können Sie dies alternativ tun und eine überladene Version aufrufen, für
deserialize()
die das dritte Argument erforderlich ist . Objekt zu deserialisieren.Eine andere Möglichkeit, die funktionieren könnte (aber nicht 100% sicher ist), wäre die Angabe von
Converter
object (@JsonDeserialize(converter=MyConverter.class)
). Dies ist eine neue Jackson 2.2-Funktion. In Ihrem Fall würde Converter den Typ nicht konvertieren, sondern das Ändern des Objekts vereinfachen. Ich weiß jedoch nicht, ob Sie damit genau das tun können, was Sie möchten, da der Standard-Deserializer zuerst aufgerufen wird und erst dann IhrConverter
.quelle
BeanDeserializerModifier
ist der Callback-Handler, der dies ermöglicht.Wenn Sie eine zusätzliche Benutzerklasse deklarieren können, können Sie diese nur mithilfe von Anmerkungen implementieren
quelle
Hier ist ein Oneliner mit ObjectMapper
Und bitte: Es ist wirklich nicht nötig, einen String-Wert oder etwas anderes zu verwenden. Alle benötigten Informationen werden von JsonParser bereitgestellt. Verwenden Sie sie daher.
quelle
In Anlehnung an die Vorschläge von Tomáš Záluský können Sie in Fällen, in denen die Verwendung
BeanDeserializerModifier
unerwünscht ist, selbst einen Standard-Deserializer erstellenBeanDeserializerFactory
, obwohl einige zusätzliche Einstellungen erforderlich sind. Im Kontext würde diese Lösung folgendermaßen aussehen:quelle
Ich war mit der Verwendung nicht einverstanden,
BeanSerializerModifier
da es gezwungen ist, einige Verhaltensänderungen im zentralenObjectMapper
und nicht im benutzerdefinierten Deserializer selbst zu deklarieren, und tatsächlich ist es eine parallele Lösung zum Annotieren von Entitätsklassen mitJsonSerialize
. Wenn Sie es ähnlich sehen, können Sie meine Antwort hier schätzen: https://stackoverflow.com/a/43213463/653539quelle
Eine einfachere Lösung für mich bestand darin, einfach eine weitere Bean hinzuzufügen
ObjectMapper
und diese zum Deserialisieren des Objekts zu verwenden (dank https://stackoverflow.com/users/1032167/varren Kommentar) - in meinem Fall war ich daran interessiert, entweder seine ID zu deserialisieren (ein int) oder das gesamte Objekt https://stackoverflow.com/a/46618193/986160Für jede Entität, die einen benutzerdefinierten Deserializer durchlaufen muss, müssen wir sie
ObjectMapper
in meinem Fall in der globalen Bean der Spring Boot App konfigurieren (z. B. fürCategory
):quelle
Sie müssen scheitern, wenn Sie versuchen, Ihren benutzerdefinierten Deserializer von Grund auf neu zu erstellen.
Stattdessen müssen Sie die (vollständig konfigurierte) Standard-Deserializer-Instanz über eine benutzerdefinierte Instanz abrufen
BeanDeserializerModifier
und diese Instanz dann an Ihre benutzerdefinierte Deserializer-Klasse übergeben:Hinweis: Diese Modulregistrierung ersetzt die
@JsonDeserialize
Annotation, dh dieUser
Klasse oderUser
Felder sollten nicht mehr mit dieser Annotation annotiert werden.Der benutzerdefinierte Deserializer sollte dann auf a basieren,
DelegatingDeserializer
damit alle Methoden delegieren, es sei denn, Sie geben eine explizite Implementierung an:quelle