我有课
SomeClass
:
public class SomeClass {
public String someString = "SOME-STRING-VALUE";
public Date someDate = new Date();
}
我的目标是创建一个
Map<String, Object>
,其中包含 SomeClass
的成员名称作为键,其值作为给定 SomeClass
实例的映射中的值
为此,我使用 Jackson,如下所示:
SomeClass someObject = new SomeClass();
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(someObject); // serialize object
System.out.println(jsonString); // {"someString":"SOME-STRING-VALUE","someDate":1500138786720}
Map<String, Object> objectMap = mapper.readValue(jsonString, Map.class); // deserialize back to Map instead of SomeClass
objectMap.forEach((k, v) -> System.out.println(k + " : " + v + " : " + v.getClass()));
// This prints:
// someString : SOME-STRING-VALUE : class java.lang.String
// someDate : 1500138786720 : class java.lang.Long
因此,我使用对象映射器将对象序列化为
jsonString
,然后为了创建所需的映射,我将 jsonString
反序列化回 Map
。
这里的问题是
Date
字段被序列化为原始 long 值,而在反序列化回来时,它被反序列化为 Long
而不是 Date
,因为我将其反序列化为 Object
。如果我一直将反序列化回 SomeClass
,则 Long
值将转换为相关的 Date
值。但就我而言,在反序列化时,mapper
不知道反序列化之前someDate
的实际类型是什么,因此someDate
在Long
中被反序列化为objectMap
本身。
似乎确实有一种方法可以让映射器在序列化之前知道
jsonString
中给定字段的类型,方法是使用杰克逊多态类型处理将类型信息嵌入到
jsonString
中。为此,我在编写 jsonString
之前启用了默认输入:
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
我现在得到的
jsonString
是:
["some.package.name.here.SomeClass",{"someString":"SOME-STRING-VALUE","someDate":["java.util.Date",1500140185993]}]
因此
jsonString
现在包含 someDate
字段的类型。但我很难将其中包含类型信息的jsonString
读回我的objectMap
。我遇到异常:
java.lang.IllegalArgumentException:类 some.package.name.here.SomeClass 不是[地图类型的子类型; class java.util.Map, [简单类型, 类 java.lang.Object] -> [简单类型,类 java.lang.Object]]
我是否需要将
Map<String, Object> objectMap = mapper.readValue(jsonString, Map.class);
行更改为其他内容,让mapper
知道它要读取的字符串不是普通字符串,而是嵌入了类型信息的字符串。映射器不应该已经知道这一点吗,因为我们用来读取 jsonString
的映射器对象与编写它的映射器对象完全相同?
我发现的所有示例都遵循继承层次结构示例,在阅读时它们只会传递 BaseClass,但我无法弄清楚我应该为我的情况做什么。
如何将
someDate
字段从 Date
转换回 Long
,同时将其放入具有 Object
类型值的地图中?
带有类型参考
Map<String, Date> map = mapper.readValue(json, new TypeReference<Map<String, Date>>() {});
您可以为映射中的所有值提供类型,但实际上对于具有不同类型属性的映射,您的 SomeClass 提供了用于反序列化此映射的结构和必要的类型信息,因此我只需使用 SomeClass 进行反序列化,然后如果您需要映射,则创建结果对象属性的映射,例如与
Map<String, Object> map = new HashMap<>();
Field[] fields = object.getClass().getDeclaredFields();
for (Field field: fields) {
field.setAccessible(true);
map.put(field.getName(), field.get(object));
}