Tengo un problema en mi deserializador personalizado en Jackson. Quiero acceder al serializador predeterminado para completar el objeto en el que estoy deserializando. Después de la población, haré algunas cosas personalizadas, pero primero quiero deserializar el objeto con el comportamiento predeterminado de Jackson.
Este es el código que tengo en este momento.
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;
}
}
Lo que necesito es una forma de inicializar el deserializador predeterminado para poder rellenar previamente mi POJO antes de iniciar mi lógica especial.
Al llamar a deserializar desde dentro del deserializador personalizado, parece que el método se llama desde el contexto actual sin importar cómo construya la clase de serializador. Por la anotación en mi POJO. Esto provoca una excepción de desbordamiento de pila por razones obvias.
He intentado inicializar un, BeanDeserializer
pero el proceso es extremadamente complejo y no he logrado encontrar la manera correcta de hacerlo. También intenté sobrecargar AnnotationIntrospector
sin éxito, pensando que podría ayudarme a ignorar la anotación en DeserializerContext
. Finalmente, parece que podría haber tenido cierto éxito con el uso, JsonDeserializerBuilders
aunque esto me obligó a hacer algunas cosas mágicas para obtener el contexto de la aplicación de Spring. Agradecería cualquier cosa que pudiera llevarme a una solución más limpia, por ejemplo, ¿cómo puedo construir un contexto de deserialización sin leer la JsonDeserializer
anotación?
DeserializationContext
no es algo que debas crear o cambiar; será proporcionado porObjectMapper
.AnnotationIntrospector
, del mismo modo, no será de ayuda para obtener acceso.Respuestas:
Como StaxMan ya sugirió, puede hacer esto escribiendo un
BeanDeserializerModifier
y registrándolo a través deSimpleModule
. El siguiente ejemplo debería funcionar:fuente
JsonSerializer
? Tengo varios serializadores pero tienen código en común, así que quiero generarlo. Intento llamar directamente al serializador, pero el resultado no se desenvuelve en el resultado JSON (cada llamada al serializador crea un nuevo objeto)BeanSerializerModifier
,ResolvableSerializer
yContextualSerializer
son las interfaces coincidentes que se utilizarán para la serialización.readTree()
pero la respuesta no. ¿Cuál es la ventaja de este enfoque frente al publicado por Derek Cochran ? ¿Hay alguna manera de hacer que esto funcionereadTree()
?Encontré una respuesta en ans que es mucho más legible que la respuesta aceptada.
Realmente no hay nada más fácil que esto.
fuente
StackOverflowError
, ya que Jackson usará el mismo serializador nuevamente paraUser
...El
DeserializationContext
tiene unreadValue()
método que puede usar. Esto debería funcionar tanto para el deserializador predeterminado como para cualquier deserializador personalizado que tenga.Solo asegúrese de llamar
traverse()
alJsonNode
nivel que desea leer para recuperar elJsonParser
para pasarreadValue()
.fuente
Hay un par de formas de hacer esto, pero hacerlo bien implica un poco más de trabajo. Básicamente, no puede utilizar la subclasificación, ya que la información que necesitan los deserializadores predeterminados se crea a partir de definiciones de clases.
Entonces, lo que probablemente pueda usar es construir un
BeanDeserializerModifier
registro a través de laModule
interfaz (usoSimpleModule
). Debe definir / anularmodifyDeserializer
, y para el caso específico en el que desea agregar su propia lógica (donde el tipo coincide), construya su propio deserializador, pase el deserializador predeterminado que se le proporciona. Y luego, en eldeserialize()
método, puede simplemente delegar la llamada, tomar el resultado Object.Alternativamente, si realmente debe crear y completar el objeto, puede hacerlo y llamar a la versión sobrecargada
deserialize()
que toma el tercer argumento; objeto para deserializar.Otra forma que podría funcionar (pero no 100% segura) sería especificar
Converter
object (@JsonDeserialize(converter=MyConverter.class)
). Esta es una nueva característica de Jackson 2.2. En su caso, Converter no convertiría el tipo en realidad, sino que simplificaría la modificación del objeto: pero no sé si eso le permitiría hacer exactamente lo que desea, ya que el deserializador predeterminado se llamaría primero, y solo luego suConverter
.fuente
BeanDeserializerModifier
es el controlador de devolución de llamada que permite eso.Si es posible que declare una clase de usuario adicional, puede implementarla simplemente usando anotaciones
fuente
Aquí hay un delineador usando ObjectMapper
Y por favor: Realmente no hay necesidad de usar ningún valor de cadena o algo más. Toda la información necesaria la proporciona JsonParser, así que úsela.
fuente
En la línea de lo que ha sugerido Tomáš Záluský , en los casos en que el uso no
BeanDeserializerModifier
sea deseable, puede construir un deserializador predeterminado usted mismo usandoBeanDeserializerFactory
, aunque es necesaria una configuración adicional. En contexto, esta solución se vería así:fuente
No estaba de acuerdo con el uso,
BeanSerializerModifier
ya que obliga a declarar algunos cambios de comportamientoObjectMapper
en el deserializador central en lugar del personalizado y, de hecho, es una solución paralela para anotar la clase de entidad conJsonSerialize
. Si lo siente de manera similar, puede apreciar mi respuesta aquí: https://stackoverflow.com/a/43213463/653539fuente
Una solución más simple para mí fue simplemente agregar otro bean de
ObjectMapper
y usarlo para deserializar el objeto (gracias a https://stackoverflow.com/users/1032167/varren comentario); en mi caso, estaba interesado en deserializar a su id (un int) o el objeto completo https://stackoverflow.com/a/46618193/986160para cada entidad que necesita pasar por un deserializador personalizado, debemos configurarlo en el
ObjectMapper
bean global de la aplicación Spring Boot en mi caso (por ejemplo, paraCategory
):fuente
Es probable que falle si intenta crear su deserializador personalizado desde cero.
En su lugar, debe obtener la instancia de deserializador predeterminada (completamente configurada) a través de una instancia personalizada
BeanDeserializerModifier
y luego pasar esta instancia a su clase de deserializador personalizada:Nota: El registro de este módulo reemplaza la
@JsonDeserialize
anotación, es decir, laUser
clase o losUser
campos ya no deben anotarse con esta anotación.El deserializador personalizado debe basarse en un
DelegatingDeserializer
para que todos los métodos deleguen, a menos que proporcione una implementación explícita:fuente