首页 文章

如何从Jackson中的自定义反序列化器调用默认反序列化器

提问于
浏览
74

我在 Jackson 的自定义反序列化器中遇到了问题 . 我想访问默认的序列化程序来填充我反序列化的对象 . 在人口之后,我将做一些自定义的事情,但首先我想用默认的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中有注释 . 这会导致Stack Overflow异常,原因很明显 . 我已经尝试初始化beandeserializer,但是这个过程非常复杂,我还没有找到正确的方法来完成它 . 我也尝试重载注释内部跟踪器无济于事,认为它可以帮助我忽略DeserializerContext中的注释 . 最后它接缝我可能使用JsonDeserializerBuilders取得了一些成功,虽然这要求我做一些神奇的东西来从 Spring 天获得应用程序上下文 . 我将不胜感激,这可能会让我找到一个更清晰的解决方案,例如如何在不读取JsonDeserializer注释的情况下构建反序列化上下文 .

8 回答

  • 0

    正如StaxMan已经建议你可以写一个 BeanDeserializerModifier 并通过 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);
      }
    }
    
  • 0

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

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

    或者,如果您必须实际创建并填充对象,则可以执行此操作并调用带有第三个参数的 deserialize() 的重载版本;反序列化的对象 .

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

  • 1

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

    只需确保在要读取的 JsonNode 级别上调用 traverse() 以检索 JsonParser 以传递给 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;
        }
    
    }
    
  • 73

    如果您可以声明额外的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;
      }
    
    }
    
  • 5

    对我来说更简单的解决方案是添加另一个 ObjectMapper 的bean并使用它反序列化对象(感谢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;
         }
    }
    

    对于需要通过自定义反序列化器的每个实体,我需要在我的情况下在Spring Boot App的全局 ObjectMapper bean中配置它(例如 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;
    }
    
  • 4

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

    public User deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
            JsonNode tree = jp.readTree(jp);
    
            // To call the default deserializer, simply invoke:
            User user = tree.get("user").traverse(jp.getCodec()).readValueAs(User.class);
            return user;
          }
    

    它真的不容易 .

  • 8

    我不习惯使用 BeanSerializerModifier ,因为它强制在中心 ObjectMapper 中声明一些行为更改,而不是在自定义反序列化器本身中,并且实际上它是使用 JsonSerialize 注释实体类的并行解决方案 . 如果你觉得它的方式相似,你可能会在这里感谢我的回答:https://stackoverflow.com/a/43213463/653539

  • 1

    Tomáš Záluský has suggested的情况下,如果不希望使用 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();
        JsonDeserializer<Object> defaultDeserializer = BeanDeserializerFactory.instance.buildBeanDeserializer(ctxt, User.class, config.introspect(User.class));
    
        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;
    }
    

相关问题