Jackson java.util.Date Map<String, Object> 中的值(反)序列化
Jackson java.util.Date value in Map<String, Object> (de-)serialization
考虑这个属性
@JsonProperty
private Map<String, Object> myMap;
当包含的 java.util.Date
值被序列化为 long 时,它将不会再次反序列化为 Date
,因为 Map<String, Object>
中不存在类型信息。我怎样才能绕过这个问题?我阅读了有关 this question 的答案,这将是一种解决方法,但无法区分包含日期的字符串和在地图中序列化为字符串的日期。 我可以告诉 Jackson 为每个映射值包含类型信息,以便 Jackson 可以正确反序列化它们吗?
实施自定义反序列化器并将注释 @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));
}
最后,我想到了这个解决方案。反序列化器:
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);
}
映射键根据数据类型进行调整。这很容易扩展。
考虑这个属性
@JsonProperty
private Map<String, Object> myMap;
当包含的 java.util.Date
值被序列化为 long 时,它将不会再次反序列化为 Date
,因为 Map<String, Object>
中不存在类型信息。我怎样才能绕过这个问题?我阅读了有关 this question 的答案,这将是一种解决方法,但无法区分包含日期的字符串和在地图中序列化为字符串的日期。 我可以告诉 Jackson 为每个映射值包含类型信息,以便 Jackson 可以正确反序列化它们吗?
实施自定义反序列化器并将注释 @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));
}
最后,我想到了这个解决方案。反序列化器:
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);
}
映射键根据数据类型进行调整。这很容易扩展。