【问题标题】:How to restrict jackson from parsing millis to LocalDate in json request如何限制杰克逊在json请求中将millis解析为LocalDate
【发布时间】:2021-06-27 03:19:19
【问题描述】:

我需要验证 json 请求中的 LocalDate 字段。我想要的是防止将数字反序列化为本地日期的 miilis。这是一个例子:

我有一个实体:

public class Test {

   @NotNull
   @JsonFormat(pattern = "yyyy-MM-dd")
   private LocalDate birthDate;

   //getter and setter of course

}

Jackson2ObjectMapperBuilder 配置:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
    builder.featuresToEnable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
    builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
    builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    builder.modulesToInstall(new JavaTimeModule());
    return builder;
}

现在如果我收到:

{
    "birthDate": 1
}

结果是birthDate=1970-01-02

我可以通过将leniency 设置为 false 来做到这一点:

objectMapper.configOverride(LocalDate.class).setFormat(JsonFormat.Value.forLeniency(false));
objectMapper.configOverride(LocalDateTime.class).setFormat(JsonFormat.Value.forLeniency(false));

然后它通过抛出MismatchedInputException来工作

但是对于我们的服务的向后兼容性有点残酷,因为我们需要将所有日期模式从“yyyy-MM-dd”更改为“uuuu-MM-dd”,我想知道是否有一些解决方案可以说 jackson “如果在反序列化时看到数字或与模式不同的任何内容,则抛出异常”

【问题讨论】:

    标签: java json-deserialization jsr310


    【解决方案1】:

    您可以编写自定义 LocalDateDeserializer:

    public class MyLocalDateDeserializer extends JsonDeserializer<LocalDate> implements ContextualDeserializer {
    
        private LocalDateDeserializer defaultDeserializer = new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    
        public MyLocalDateDeserializer() {
            super();
        }
    
        public MyLocalDateDeserializer(LocalDateDeserializer defaultDeserializer) {
            super();
            this.defaultDeserializer = defaultDeserializer;
        }
    
    
        @Override
        public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException
        {
            if (StringUtils.isNumeric(parser.getText())) {
                throw  JsonMappingException.from(parser, "Not a String representation of Date ");
    
            }
            return defaultDeserializer.deserialize(parser, context);
        }
    
    
        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
                BeanProperty property) throws JsonMappingException
        {
            JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType());
            return (format == null) ? this : new MyLocalDateDeserializer(new LocalDateDeserializer(DateTimeFormatter.ofPattern(format.getPattern())));
        }
    
        protected JsonFormat.Value findFormatOverrides(DeserializationContext ctxt,
                BeanProperty prop, Class<?> typeForDefaults)
        {
            if (prop != null) {
                return prop.findPropertyFormat(ctxt.getConfig(), typeForDefaults);
            }
            // even without property or AnnotationIntrospector, may have type-specific defaults
            return ctxt.getDefaultPropertyFormat(typeForDefaults);
        }
    
    }
    

    并在需要时注册。

    这是我的简单测试:

    @Test()
    public void testObjectMapperForLocalDate() throws IOException {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDate.class, new MyLocalDateDeserializer());
        builder.modulesToInstall(javaTimeModule);
        ObjectMapper objectMapper =  builder.build();
    
           DateContainer container =  objectMapper.readValue("{\r\n" +
                    "    \"birthDate\": \"1999-01-01\"\r\n" +
                    "}", DateContainer.class);
               System.out.println(container.getBirthDate());
    }
    
    @Test()
    public void testFailObjectMapperForLocalDate() throws IOException {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDate.class, new MyLocalDateDeserializer());
        builder.modulesToInstall(javaTimeModule);
        ObjectMapper objectMapper =  builder.build();
    
        assertThrows(JsonMappingException.class, () -> {
           DateContainer container =  objectMapper.readValue("{\r\n" +
                    "    \"birthDate\": 1\r\n" +
                    "}", DateContainer.class);
               System.out.println(container.getBirthDate());
          });
    }
    

    编辑

    反序列化器使用模式

    【讨论】:

    • 感谢您的回答!这就是问题所在,我们的服务中有不同的日期模式,因此很遗憾,不能选择使用 Pattern 来忽略注释
    • @Nikita 我喜欢学习,现在它可以处理 Pattern
    • 这真是太棒了!非常感谢你的魔法课!现在它可以正常工作了
    猜你喜欢
    • 1970-01-01
    • 2019-07-27
    • 2019-11-25
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    • 2021-05-27
    • 2020-10-06
    • 2013-12-29
    相关资源
    最近更新 更多