¿Cómo llamo al deserializador predeterminado desde un deserializador personalizado en Jackson?
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 completar, 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 completar previamente mi POJO antes de iniciar mi lógica especial.
Al llamar a deserialize desde el deserializador personalizado, parece que el método se llama desde el contexto actual sin importar cómo construya la clase de serializador. Debido a 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 el AnnotationIntrospector
archivo sin éxito, pensando que podría ayudarme a ignorar la anotación en el archivo DeserializerContext
. Finalmente, parece que podría haber tenido cierto éxito al usarlo, JsonDeserializerBuilders
aunque esto requirió que hiciera algunas cosas mágicas para obtener el contexto de la aplicación desde Spring. Agradecería cualquier cosa que pueda 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?
Como ya sugirió StaxMan, puede hacerlo escribiendo un BeanDeserializerModifier
y registrándolo a través de SimpleModule
. El siguiente ejemplo debería funcionar:
public class UserEventDeserializer extends StdDeserializer<User> implements ResolvableDeserializer
{
private static final long serialVersionUID = 7923585097068641765L;
private final JsonDeserializer<?> defaultDeserializer;
public UserEventDeserializer(JsonDeserializer<?> defaultDeserializer)
{
super(User.class);
this.defaultDeserializer = defaultDeserializer;
}
@Override public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
User deserializedUser = (User) defaultDeserializer.deserialize(jp, ctxt);
// Special logic
return deserializedUser;
}
// for some reason you have to implement ResolvableDeserializer when modifying BeanDeserializer
// otherwise deserializing throws JsonMappingException??
@Override public void resolve(DeserializationContext ctxt) throws JsonMappingException
{
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException
{
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier()
{
@Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)
{
if (beanDesc.getBeanClass() == User.class)
return new UserEventDeserializer(deserializer);
return deserializer;
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
User user = mapper.readValue(new File("test.json"), User.class);
}
}
Tiene DeserializationContext
un readValue()
método que puede utilizar. Esto debería funcionar tanto para el deserializador predeterminado como para cualquier deserializador personalizado que tenga.
Sólo asegúrese de llamar traverse()
al JsonNode
nivel que desea leer para recuperar el JsonParser
mensaje al que desea pasar readValue()
.
public class FooDeserializer extends StdDeserializer<FooBean> {
private static final long serialVersionUID = 1L;
public FooDeserializer() {
this(null);
}
public FooDeserializer(Class<FooBean> t) {
super(t);
}
@Override
public FooBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
FooBean foo = new FooBean();
foo.setBar(ctxt.readValue(node.get("bar").traverse(), BarBean.class));
return foo;
}
}