【问题标题】:Why ObjectMapper changes Date type into Long为什么 ObjectMapper 将 Date 类型更改为 Long
【发布时间】:2020-06-06 02:06:19
【问题描述】:

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

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

我在使用日期字段时遇到问题,因为在地图中它们正在变成长对象。

我添加了反序列化器,如ObjectMapper changes Date to String

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 属性。似乎是类似的问题,但这两种类型对我进一步的工作没有太大的区别。

【问题讨论】:

    标签: java serialization jackson mapping deserialization


    【解决方案1】:

    您应该使用 POJO 来表示您的数据,而不是 Map。由于Map 是模棱两可的键值对,因此每个字段都没有类型信息,Jackson 将读取值而不对其类型信息进行任何推断。通过在定义的结构中对数据进行建模,Jackson 会将值读取到定义的类型中。

    public class DataTransferObject
    {
       Date date;
       BigDecimal decimal;
    
       public Date getDate ()
       {
          return date;
       }
    
       public void setDate ( Date date )
       {
          this.date = date;
       }
    
       public BigDecimal getDecimal ()
       {
          return decimal;
       }
    
       public void setDecimal ( BigDecimal decimal )
       {
          this.decimal = decimal;
       }
    }
    
    objectMapper.convertValue(obj, DataTransferObject.class);
    

    通过这种方法,您可以使用 Jackson 的序列化功能来转换使用字符串或整数的日期。不需要自定义(反)序列化程序。

    【讨论】:

    • 抱歉,您的提议对我没有任何价值。我不是要求解决方法。我想要一个地图,其中包含一个 Date 类型的条目。我对 ObjectMapper 如何做到这一点很感兴趣。你的回答是不能。然后应该在某个地方有可以使用的类型列表。因为 Double 不是字符串,它可以使用。请问,您能否提供一些支持您想法的参考资料?
    • “通过在定义的结构中对数据建模,Jackson 会将值读入定义的类型。” - 不是这样。我的对象已经有属性而不是简单的字段。 “映射是模棱两可的键值对”-不必如此-对象可以保留其类型信息。失去它的是杰克逊 ObjectMapper。 “你应该使用 POJO 来表示你的数据,而不是地图” - 你怎么能说出来,不知道我为什么需要那张地图?真的,你宁愿删除那个答案。
    【解决方案2】:

    因为您将 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;
                }
            }
        }
    

    另外,不要忘记将.addDeserializer 的第一个参数替换为Object.class

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

    【讨论】:

    • 这看起来很有希望且始终如一。我赞成,对不起,我会在星期一检查它是如何工作的。谢谢。
    • 非常感谢。 ObjectMapper 确实丢失了对象的类型。可以通过将类信息添加到其序列化的序列化程序来解决这个问题。但我认为,最好的方法就是永远不要使用那个令人作呕的危险功能。在这里 (stackoverflow.com/questions/6796187/…) 可以找到更好的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-15
    • 1970-01-01
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 2017-01-20
    • 2022-01-06
    相关资源
    最近更新 更多