¿Cómo llamo al deserializador predeterminado desde un deserializador personalizado en Jackson?

Resuelto Pablo Jomer asked hace 11 años • 11 respuestas

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 BeanDeserializerpero el proceso es extremadamente complejo y no he logrado encontrar la manera correcta de hacerlo. También intenté sobrecargar el AnnotationIntrospectorarchivo 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, JsonDeserializerBuildersaunque 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 JsonDeserializeranotación?

Pablo Jomer avatar Aug 19 '13 19:08 Pablo Jomer
Aceptado

Como ya sugirió StaxMan, puede hacerlo escribiendo un BeanDeserializerModifiery 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);
  }
}
schummar avatar Aug 23 '2013 14:08 schummar

Tiene DeserializationContextun 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 JsonNodenivel que desea leer para recuperar el JsonParsermensaje 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;
    }

}
Derek Cochran avatar May 01 '2018 20:05 Derek Cochran