【问题标题】:Jackson java.util.Date value in Map<String, Object> (de-)serializationMap<String, Object> 中的 Jackson java.util.Date 值(反)序列化
【发布时间】:2018-08-24 10:01:03
【问题描述】:

考虑这个属性

@JsonProperty
private Map<String, Object> myMap;

当包含的java.util.Date 值被序列化时,它不会再次反序列化为Date,因为Map&lt;String, Object&gt; 中不存在类型信息。我怎样才能绕过这个问题?我阅读了有关this question 的答案,这将是一种解决方法,但无法区分包含日期的字符串与在地图中序列化为字符串的日期。 我可以告诉 Jackson 包含每个映射值的类型信息,以便 Jackson 可以正确反序列化它们吗?

【问题讨论】:

  • JSON 中没有类型信息。使用普通的Object,杰克逊无法区分实际的多头日期。如果可能的话,您可以为 Object 创建一个包含 java 类型信息的包装器,并使用它来反序列化实际值,或者(如果没有选项)也许可以将日期格式化为 ISO-8601 字符串?这样,您可以检查值以匹配预期模式并转换为日期。
  • @dpr 是的,我将尝试使用类似 "[date]" 的东西为映射键添加后缀,然后将其反序列化为日期。
  • 也许这个链接对你有用。 Jackson docs

标签: java jackson jackson-databind


【解决方案1】:

最后,我想出了这个解决方案。反序列化器:

private TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {
};

@Override
public Map<String, Object> deserialize(JsonParser p, DeserializationContext ctxt, Map<String, Object> target) throws IOException {
    Map<String, Object> map = new ObjectMapper().readValue(p, typeRef);

    for (Map.Entry<String, Object> e : map.entrySet()) {
        if (e.getKey().endsWith("[date]")) {
            target.put(e.getKey().substring(0, e.getKey().length() - 6), new Date((Long) e.getValue()));
        }
        else {
            target.put(e.getKey(), e.getValue());
        }
    }

    return target;
}

序列化器:

@Override
public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    Map<String, Object> adaptedValue = new HashMap<>(value);

    for (Map.Entry<String, Object> e : value.entrySet()) {
        if (e.getValue() instanceof Date) {
            adaptedValue.put(e.getKey() + "[date]", ((Date) e.getValue()).getTime());
            adaptedValue.remove(e.getKey());
        }
    }

    new ObjectMapper().writeValue(gen, adaptedValue);
}

映射键根据数据类型进行调整。这很容易扩展。

【讨论】:

    【解决方案2】:

    实现自定义反序列化器并将注释@JsonDeserialize(using = DateDeserializer.class) 添加到您的字段。

    看看这个例子:

    你的 Json-Bean

    public class Foo {
    
        private String            name;
    
        @JsonProperty
        @JsonDeserialize(using = DateDeserializer.class)
        private Map<String, Object> dates;
    
        [...] // getter, setter, equals, hashcode
    }
    

    反序列化器

    public class DateDeserializer extends JsonDeserializer<Map<String, Object>> {
    
        private TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {};
    
        @Override
        public Map<String, Object> deserialize(JsonParser p, DeserializationContext ctxt, Map<String, Object> target) throws IOException, JsonProcessingException {
    
            Map<String, Long> map = new ObjectMapper().readValue(p, typeRef);
    
            for(Entry<String, Long> e : map.entrySet()){
    
                Long value = e.getValue();
                String key = e.getKey();
    
                if(value instanceof Long){ // or if("date".equals(key)) ...
                    target.put(key, new Date(value));
                } else {
                    target.put(key, value); // leave as is
                }
    
            }
    
            return target;
        }
    
        @Override
        public Map<String, Object> deserialize(JsonParser paramJsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return this.deserialize(paramJsonParser, ctxt, new HashMap<>());
        }
    
    }
    

    简单测试

    public static void main(String[] args) throws Exception {
    
        Foo foo1 = new Foo();
        foo1.setName("foo");
        foo1.setData(new HashMap<String, Object>(){{
            put("date",   new Date());
            put("bool",   true);
            put("string", "yeah");
        }});
        ObjectMapper mapper = new ObjectMapper();
        String jsonStr = mapper.writeValueAsString(foo1);
        System.out.println(jsonStr);
        Foo foo2 = mapper.readValue(jsonStr, Foo.class);
    
        System.out.println(foo2.equals(foo1));
    
    }
    

    【讨论】:

    • 感谢您的回答,但在我的情况下,地图由字符串、布尔值、数字和日期组成。你对这个案子有什么想法吗?
    • @SteffenHarbich 在这种情况下使用Map&lt;String, Object&gt;。我已经更新了我的答案。
    • 好的,我想我会尝试编写一个序列化器/反序列化器对,根据类型(仅在日期的情况下)添加/解释映射键的一些后缀或前缀,这样对我的其余代码是透明的。明天我会给你反馈。到目前为止谢谢!
    猜你喜欢
    • 2014-05-19
    • 1970-01-01
    • 2021-09-07
    • 2017-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    相关资源
    最近更新 更多