130

我在杰克逊的自定义反序列化器中有问题。我想访问默认序列化程序来填充我要反序列化的对象。在填充之后,我会做一些自定义的事情,但首先我想用默认的 Jackson 行为反序列化对象。

这是我目前拥有的代码。

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;
  }

}

我需要的是一种初始化默认解串器的方法,这样我就可以在开始我的特殊逻辑之前预先填充我的 POJO。

从自定义反序列化器中调用反序列化时无论我如何构造序列化器类,似乎都是从当前上下文调用该方法。因为我的 POJO 中的注释。由于显而易见的原因,这会导致堆栈溢出异常。

我尝试过初始化 aBeanDeserializer但过程非常复杂,而且我还没有找到正确的方法来完成它。我也尝试过重载AnnotationIntrospector无济于事,认为它可能会帮助我忽略DeserializerContext. 最后它接缝我可能已经使用了一些成功,JsonDeserializerBuilders尽管这需要我做一些神奇的事情来从 Spring 中获取应用程序上下文。我将不胜感激任何可以使我找到更清洁的解决方案的事情,例如如何在不阅读JsonDeserializer注释的情况下构造反序列化上下文。

4

11 回答 11

105

正如 StaxMan 已经建议的那样,您可以通过编写 aBeanDeserializerModifier并通过SimpleModule. 以下示例应该有效:

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);
  }
}
于 2013-08-23T14:56:45.213 回答
19

DeserializationContext有一个readValue()你可以使用的方法。这应该适用于默认反序列化器和您拥有的任何自定义反序列化器。

只需确保调用您要读取traverse()的级别以检索要传递到的。JsonNodeJsonParserreadValue()

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;
    }

}
于 2018-05-01T20:22:38.180 回答
18

我在https://stackoverflow.com/a/51927577/14731找到了一个答案,它比接受的答案更具可读性。

public User deserialize(JsonParser jp, DeserializationContext ctxt)
    throws IOException, JsonProcessingException {
        User user = jp.readValueAs(User.class);
         // some code
         return user;
      }

真的没有比这更容易的了。

于 2018-11-04T02:33:10.453 回答
10

如果您可以声明额外的 User 类,那么您可以仅使用注释来实现它

// your class
@JsonDeserialize(using = UserEventDeserializer.class)
public class User {
...
}

// extra user class
// reset deserializer attribute to default
@JsonDeserialize
public class UserPOJO extends User {
}

public class UserEventDeserializer extends StdDeserializer<User> {

  ...
  @Override
  public User deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
    // specify UserPOJO.class to invoke default deserializer
    User deserializedUser = jp.ReadValueAs(UserPOJO.class);
    return deserializedUser;

    // or if you need to walk the JSON tree

    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    JsonNode node = oc.readTree(jp);
    // specify UserPOJO.class to invoke default deserializer
    User deserializedUser = mapper.treeToValue(node, UserPOJO.class);

    return deserializedUser;
  }

}
于 2018-01-17T10:45:32.983 回答
9

有几种方法可以做到这一点,但要做到这一点需要更多的工作。基本上你不能使用子类,因为默认反序列化器需要的信息是从类定义中构建的。

所以你最有可能使用的是构造一个BeanDeserializerModifier,通过Module接口注册它(使用SimpleModule)。您需要定义/覆盖modifyDeserializer,对于要添加自己的逻辑(类型匹配)的特定情况,构建自己的反序列化器,传递给定的默认反序列化器。然后在deserialize()方法中你可以委托调用,获取结果对象。

或者,如果您必须实际创建和填充对象,您可以这样做并调用带有deserialize()第三个参数的重载版本;要反序列化的对象。

另一种可能有效(但不是 100% 肯定)的方法是指定Converter对象 ( @JsonDeserialize(converter=MyConverter.class))。这是 Jackson 2.2 的新功能。在您的情况下, Converter 实际上不会转换类型,而是简化修改对象:但我不知道这是否会让您完全按照您的意愿行事,因为将首先调用默认的反序列化器,然后才调用您的Converter.

于 2013-08-21T19:23:52.093 回答
5

按照Tomáš Záluský 的建议,在不希望使用的情况下,BeanDeserializerModifier您可以使用自己构建一个默认的反序列化器BeanDeserializerFactory,尽管需要进行一些额外的设置。在上下文中,此解决方案如下所示:

public User deserialize(JsonParser jp, DeserializationContext ctxt)
  throws IOException, JsonProcessingException {

    ObjectCodec oc = jp.getCodec();
    JsonNode node = oc.readTree(jp);
    User deserializedUser = null;

    DeserializationConfig config = ctxt.getConfig();
    JavaType type = TypeFactory.defaultInstance().constructType(User.class);
    JsonDeserializer<Object> defaultDeserializer = BeanDeserializerFactory.instance.buildBeanDeserializer(ctxt, type, config.introspect(type));

    if (defaultDeserializer instanceof ResolvableDeserializer) {
        ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
    }

    JsonParser treeParser = oc.treeAsTokens(node);
    config.initialize(treeParser);

    if (treeParser.getCurrentToken() == null) {
        treeParser.nextToken();
    }

    deserializedUser = (User) defaultDeserializer.deserialize(treeParser, context);

    return deserializedUser;
}
于 2017-11-13T22:17:06.813 回答
3

如果您尝试从头开始创建自定义反序列化器,您一定会失败。

相反,您需要通过 custom 获取(完全配置的)默认反序列化器实例BeanDeserializerModifier,然后将此实例传递给您的自定义反序列化器类:

public ObjectMapper getMapperWithCustomDeserializer() {
    ObjectMapper objectMapper = new ObjectMapper();

    SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier() {
        @Override
        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                    BeanDescription beanDesc, JsonDeserializer<?> defaultDeserializer) 
            if (beanDesc.getBeanClass() == User.class) {
                return new UserEventDeserializer(defaultDeserializer);
            } else {
                return defaultDeserializer;
            }
        }
    });
    objectMapper.registerModule(module);

    return objectMapper;
}

注意:此模块注册替换了@JsonDeserialize注解,即User类或User字段不应再使用此注解进行注解。

然后,自定义反序列化器应基于 aDelegatingDeserializer以便所有方法委托,除非您提供显式实现:

public class UserEventDeserializer extends DelegatingDeserializer {

    public UserEventDeserializer(JsonDeserializer<?> delegate) {
        super(delegate);
    }

    @Override
    protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegate) {
        return new UserEventDeserializer(newDelegate);
    }

    @Override
    public User deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException {
        User result = (User) super.deserialize(p, ctxt);

        // add special logic here

        return result;
    }
}
于 2019-05-01T15:02:49.460 回答
1

我不习惯使用,BeanSerializerModifier因为它强制在中央ObjectMapper而不是在自定义反序列化器本身中声明一些行为变化,实际上它是用JsonSerialize. 如果您有类似的感觉,您可能会在这里欣赏我的回答:https ://stackoverflow.com/a/43213463/653539

于 2017-04-04T17:17:36.027 回答
1

使用BeanDeserializerModifier效果很好,但如果你需要使用JsonDeserialize,有一种方法可以这样做AnnotationIntrospector

ObjectMapper originalMapper = new ObjectMapper();
ObjectMapper copy = originalMapper.copy();//to keep original configuration
copy.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {

            @Override
            public Object findDeserializer(Annotated a) {
                Object deserializer = super.findDeserializer(a);
                if (deserializer == null) {
                    return null;
                }
                if (deserializer.equals(MyDeserializer.class)) {
                    return null;
                }
                return deserializer;
            }
});

现在复制的映射器现在将忽略您的自定义反序列化器(MyDeserializer.class)并使用默认实现。您可以在deserialize自定义反序列化器的方法中使用它,通过将复制的映射器设为静态来避免递归,或者如果使用 Spring,则将其连接起来。

于 2020-08-24T09:56:11.587 回答
0

这是使用默认 ObjectMapper 的简短解决方案

private static final ObjectMapper MAPPER = new ObjectMapper(); // use default mapper / mapper without customization

public MyObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    MyObject object = MAPPER.readValue(p, MyObject.class);
    // do whatever you want 
    return object;
}

并且请:真的不需要使用任何字符串值或其他东西。所有需要的信息都由 JsonParser 提供,所以使用它。

于 2019-08-22T07:53:01.783 回答
0

对我来说一个更简单的解决方案是添加另一个 beanObjectMapper并使用它来反序列化对象(感谢https://stackoverflow.com/users/1032167/varren评论) - 在我的情况下,我有兴趣反序列化到它的 id (一个 int)或整个对象https://stackoverflow.com/a/46618193/986160

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.springframework.context.annotation.Bean;

import java.io.IOException;

public class IdWrapperDeserializer<T> extends StdDeserializer<T> {

    private Class<T> clazz;

    public IdWrapperDeserializer(Class<T> clazz) {
        super(clazz);
        this.clazz = clazz;
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        return mapper;
    }

    @Override
    public T deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
        String json = jp.readValueAsTree().toString();
          // do your custom deserialization here using json
          // and decide when to use default deserialization using local objectMapper:
          T obj = objectMapper().readValue(json, clazz);

          return obj;
     }
}

对于每个需要通过自定义反序列化器的实体,ObjectMapper在我的例子中,我们需要在 Spring Boot App 的全局 bean 中配置它(例如 for Category):

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
            mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
            mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    SimpleModule testModule = new SimpleModule("MyModule")
            .addDeserializer(Category.class, new IdWrapperDeserializer(Category.class))

    mapper.registerModule(testModule);

    return mapper;
}
于 2017-10-07T08:38:04.487 回答