首页 > 解决方案 > 为什么 ObjectMapper 将 Date 类型更改为 Long

问题描述

我正在尝试使用 Jackson ObjectMapper 从对象中获取地图:

    ObjectMapper oMapper = ObjectMapperWithDate.getObjectMapper();
    Map<String, Object> map = oMapper.convertValue(obj, Map.class);

我对日期字段有疑问,因为在地图中它们正在变成长对象。

我添加了反序列化器,如ObjectMapper 将日期更改为字符串

public class ObjectMapperWithDate {
    @Bean
    public static ObjectMapper getObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.registerModule(
                new SimpleModule("foo")
                        .addDeserializer(Date.class, new DateDeserializer())
                        .addSerializer(Date.class, new DateSerializer())
        );
        return mapper;
    }
    public static class DateSerializer extends StdScalarSerializer<Date> {
        public DateSerializer() {
            super(Date.class);
        }
        @Override
        public void serialize(Date value, JsonGenerator gen, SerializerProvider provider)
                throws IOException {
            DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
            String output = formatter.format(value);
            gen.writeString(output);
        }
    }
    public static class DateDeserializer extends StdScalarDeserializer<Date> {
        public DateDeserializer() {
            super(Date.class);
        }
        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException {
            try {
                DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
                return formatter.parse(p.getValueAsString());
            } catch (Exception e) {
                return null;
            }
        }
    }
}

当然,对映射器的调用看起来有点不同:

    ObjectMapper oMapper = ObjectMapperWithDate.getObjectMapper();
    Map<String, Object> map = oMapper.convertValue(obj, Map.class);

现在 Date 对象成为地图中的 String 对象。在其中正确表示日期。但我需要它们保持为 Date 类型。有趣的是,如果我在反序列化器中设置断点,则永远无法到达。因此,我认为永远无法到达反序列化器,这是因为序列化后的映射器根据 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 将 Date 设为 String 或 Long,并且在反序列化时永远不会识别 Date。

映射后如何让 Date 属性保持 Date 属性?我需要他们被认可。

顺便说一句,BigDecimal 属性变成了 Double 属性。这似乎是类似的问题,但这两种类型对我进一步的工作没有太大的区别。

标签: javaserializationjacksonmappingdeserialization

解决方案


因为您将 map 的值类型定义为Object,所以 Jackson 不会选择您的自定义反序列化器类型Date,而是使用其默认反序列化器将所有类型转换为基本类型(如 long、String、LinkedHashMap 等)。

如果您的对象中只有Date字段,则可以更改convertValue方法的第二个参数:

Map<String, Date> map = oMapper.convertValue(obj, new TypeReference<Map<String, Date>>() {});

但显然这不是您的情况,因此对于具有不同类型字段的对象,最直接的方法是将您的反序列化器类型更改为Object并手动解析其中的所有数据:

    public static class DateDeserializer extends StdScalarDeserializer<Object> {
        public DateDeserializer() {
            super(Object.class);
        }
        @Override
        public Object deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException {
            String valueAsString = p.getValueAsString();
            try {
                DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
                return formatter.parse(valueAsString);
            } catch (Exception e) {
                //you could add extra logic to parse other types
                return valueAsString;
            }
        }
    }

另外,不要忘记用.addDeserializerwith替换第一个参数Object.class

有关更奇特的方法,请查看这篇文章:http ://robertmarkbramprogrammer.blogspot.com/2018/05/de-serialise-json-string-to-map-with.html


推荐阅读