single custom deserializer for all objects as their ids or embedded whole objects during POST/PUT

前端 未结 3 1445
广开言路
广开言路 2020-12-19 20:29
@Entity
public Product {
   @Id
   public int id;

   public String name;

   @ManyToOne(cascade = {CascadeType.DETACH} )
   Category category

   @ManyToMany(fetch          


        
3条回答
  •  一个人的身影
    2020-12-19 21:14

    The complete solution after much struggle was - thanks to https://stackoverflow.com/users/1032167/varren's comment and https://stackoverflow.com/a/16825934/986160 I was able to use the default deserialization (through a local new objectMapper) in my StdDeserializer without the hurdles in this answer: https://stackoverflow.com/a/18405958/986160

    The code tries to parse an int and if it is a whole object it just passes it through - so it still works for example when you make a POST/PUT request of a Category or in other words when Category is not embedded

    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 extends StdDeserializer {
    
        private Class clazz;
    
        public IdWrapperDeserializer(Class 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();
    
            T obj = null;
            int id = 0;
            try {
                id = Integer.parseInt(json);
            }
            catch( Exception e) {
                obj = objectMapper().readValue(json, clazz);
                return obj;
            }
            try {
                obj = clazz.newInstance();
                ReflectionUtils.set(obj,"id",id);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return obj;
        }
    
    }
    

    for each entity I need to behave like described I need to configure it in global ObjectMapper Bean of the Spring Boot application:

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

    This is my ReflectionUtils from https://stackoverflow.com/a/14374995/986160

    public class ReflectionUtils {
        // 
        public static boolean set(Object object, String fieldName, Object fieldValue) {
            Class clazz = object.getClass();
            while (clazz != null) {
                try {
                    Field field = clazz.getDeclaredField(fieldName);
                    field.setAccessible(true);
                    field.set(object, fieldValue);
                    return true;
                } catch (NoSuchFieldException e) {
                    clazz = clazz.getSuperclass();
                } catch (Exception e) {
                    throw new IllegalStateException(e);
                }
            }
            return false;
        }
    }
    

提交回复
热议问题