How do I call the default deserializer from a custom deserializer in Jackson

前端 未结 11 1014
野趣味
野趣味 2020-11-22 14:26

I have a problem in my custom deserializer in Jackson. I want to access the default serializer to populate the object I am deserializing into. After the population I will do

相关标签:
11条回答
  • 2020-11-22 14:57

    If it is possible for you to declare extra User class then you can implement it just using annotations

    // 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;
      }
    
    }
    
    0 讨论(0)
  • 2020-11-22 14:59

    Here is a oneliner using ObjectMapper

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

    And please: There is really no need to use any String value or something else. All needed information are given by JsonParser, so use it.

    0 讨论(0)
  • 2020-11-22 15:03

    Using BeanDeserializerModifier works well, but if you need to use JsonDeserialize there is a way to do it with AnnotationIntrospector like this:

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

    Now copied mapper will now ignore your custom deserializer (MyDeserializer.class) and use default implementation. You can use it inside deserialize method of your custom deserializer to avoid recursion by making copied mapper static or wire it if using Spring.

    0 讨论(0)
  • 2020-11-22 15:07

    I found an answer at ans which is much more readable than the accepted answer.

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

    It really doesn't get easier than this.

    0 讨论(0)
  • 2020-11-22 15:07

    A simpler solution for me was to just add another bean of ObjectMapper and use that to deserialize the object (thanks to https://stackoverflow.com/users/1032167/varren comment) - in my case I was interested to either deserialize to its id (an int) or the whole object 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;
         }
    }
    

    for each entity that needs to be going through custom deserializer we need to configure it in the global ObjectMapper bean of the Spring Boot App in my case (e.g 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;
    }
    
    0 讨论(0)
提交回复
热议问题