Java 如何在嵌套贴图中使用Jackson KeyDeserializer? 问题
在现有的应用程序中,我必须反序列化可能的深度嵌套映射,并在所有级别的所有键上应用自定义Jackson反序列化程序 由于应用程序动态处理各种数据模型,我无法使用带有明确类型映射的显式数据模型。。。相反,我使用Java 如何在嵌套贴图中使用Jackson KeyDeserializer? 问题,java,json,jackson,Java,Json,Jackson,在现有的应用程序中,我必须反序列化可能的深度嵌套映射,并在所有级别的所有键上应用自定义Jackson反序列化程序 由于应用程序动态处理各种数据模型,我无法使用带有明确类型映射的显式数据模型。。。相反,我使用映射并检查反序列化的对象值本身是否为映射 这会导致自定义反序列化程序仅应用于顶级映射 更新:需要注意的是,我不能将反序列化程序绑定到所有映射,因为我的数据模型通常也有更具体的映射。对于开放式JSON设置,我通常有一个通用的Map字段;与例如Map一起生活,以获得更明确的配置位 例子 为了举例
映射
并检查反序列化的对象
值本身是否为映射
这会导致自定义反序列化程序仅应用于顶级映射
更新:需要注意的是,我不能将反序列化程序绑定到所有映射,因为我的数据模型通常也有更具体的映射。对于开放式JSON设置,我通常有一个通用的Map
字段;与例如Map
一起生活,以获得更明确的配置位
例子 为了举例说明,我提出了以下玩具示例:
ObjectMapper ObjectMapper=new ObjectMapper().registerModule(new SimpleModule())
.addKeyDeserializer(String.class,新的KeyDeserializer(){
@凌驾
公共对象反序列化密钥(字符串s,反序列化上下文反序列化上下文){
返回s.toUpperCase();
}
}));
映射值=objectMapper.readValue(
“{\'zero\':1,\'two\':{\'three\':4}}”,
新类型引用(){}
);
资产(值).containsKeys(“零”、“二”);//通过
assertThat((Map)value.get(“两”).containsKey(“三”);//失败
最后一行代码失败:
java.lang.AssertionError:
期望:
要包含密钥,请执行以下操作:
我无法将类型声明为Map
,因为int值处于同一级别。如果我尝试,我会得到:
无法反序列化java.util.LinkedHashMap
值外\u NUMBER\u INT标记的实例
问题 在本例中,即使我需要将它们声明为
对象
,但在所有嵌套贴图中获取大写键的最佳方法是什么
是否有其他方法来解决此问题?下面是一个可行的解决方案。虽然它没有使用KeyDeserializer,但正如您所发现的,KeyDeserializer不会深入到JSON中,将所有内部JSON字段名都大写 下面的解决方案使用JsonDeserializer而不是KeyDeserializer。我以您的示例为例,传递了一个更复杂的JSON来测试它。它遍历JSON中的所有对象,并遍历它遇到的所有字段名
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper().registerModule(
new SimpleModule()
.addDeserializer(Map.class, new JsonDeserializer<>() {
@Override
public Map<String, Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
var map = new HashMap<String, Object>();
while(!p.isClosed()){
JsonToken jsonToken = p.nextToken();
if (JsonToken.FIELD_NAME.equals(jsonToken)) {
String fieldName = p.getCurrentName().toUpperCase();
jsonToken = p.nextToken();
if (JsonToken.START_OBJECT.equals(jsonToken)) {
map.put(fieldName, getObject(p));
} else if (JsonToken.START_ARRAY.equals(jsonToken)) {
map.put(fieldName, getArray(p));
} else {
map.put(fieldName, getScalar(jsonToken, p));
}
}
}
return map;
}
})
);
Map<String, Object> value = objectMapper.readValue(
"{\"zero\": 1, \"two\": { \"three\": 4 }, \"four\": [\"item\", 5], \"five\": \"string\", \"six\": true, \"seven\": 1.2}",
new TypeReference<Map<String, Object>>() {}
);
assertThat(value).containsKeys("ZERO", "TWO"); // pass
assertThat((Map) value.get("TWO")).containsKey("THREE"); // pass
System.out.println("JSON = " + new GsonBuilder().setPrettyPrinting().create().toJson(value));
}
static Map<String, Object> getObject(JsonParser p) throws IOException {
var map = new HashMap<String, Object>();
JsonToken jt = p.nextToken();
while (!JsonToken.END_OBJECT.equals(jt)) {
if (JsonToken.FIELD_NAME.equals(jt)) {
String fieldName = p.getCurrentName().toUpperCase();
jt = p.nextToken();
if (JsonToken.START_OBJECT.equals(jt)) {
map.put(fieldName, getObject(p));
} else if (JsonToken.START_ARRAY.equals(jt)) {
map.put(fieldName, getArray(p));
} else {
map.put(fieldName, getScalar(jt, p));
}
}
jt = p.nextToken();
}
return map;
}
static List<Object> getArray(JsonParser p) throws IOException {
var list = new ArrayList<>();
JsonToken jt = p.nextToken();
while (!JsonToken.END_ARRAY.equals(jt)) {
if (JsonToken.START_OBJECT.equals(jt)) {
list.add(getObject(p));
} else if (JsonToken.START_ARRAY.equals(jt)) {
list.add(getArray(p));
} else {
list.add(getScalar(jt, p));
}
jt = p.nextToken();
}
return list;
}
static Object getScalar(JsonToken jsonToken, JsonParser p) throws IOException {
if (JsonToken.VALUE_NUMBER_INT.equals(jsonToken) || JsonToken.VALUE_NUMBER_FLOAT.equals(jsonToken)) {
return p.getNumberValue();
} else if (JsonToken.VALUE_FALSE.equals(jsonToken)) {
return false;
} else if (JsonToken.VALUE_TRUE.equals(jsonToken)) {
return true;
} else if (JsonToken.VALUE_STRING.equals(jsonToken)) {
return p.getValueAsString();
} else if (JsonToken.VALUE_NULL.equals(jsonToken)) {
return null;
}
throw new RuntimeException("did not find a scalar for JsonToken = " + jsonToken);
}
代码假定最外层的JSON对象是一个映射。因此,需要在JsonDeserializer中进行一次小的更新,以将数组或标量作为最外层的JSON进行处理。进一步考虑,这确实可以按预期工作,但我确实需要使用密钥序列化器。原因是我的JSON中的一些映射没有字符串键,我希望它们保持不变。将反序列化程序绑定到所有
Map
s,并假设它们总是反序列化到Map
中,如果我有一些键为枚举类型的映射,则会出现中断…我已将更新添加到问题陈述中。抱歉搞混了!我理解新的要求。在JSON中,所有键都是r字符串。您如何知道哪些键为大写,哪些键为非大写?顺便说一句,您可以创建多个ObjectMapper并更新我编写的反序列化程序的逻辑。您可以通过将反序列化器放入其自己的类中并允许设置字段/标志来确定何时对根字段进行大写来概括反序列化器,例如,因为根字段可能是枚举,不能大写。需要告知ObjectMapper它映射到的类型。因此,它只做了正确的事情,因为它被告知要创建什么类<代码>mapper.readValue(jsonString,newTypeReference(){})代码>
JSON = {
"ZERO": 1,
"FIVE": "string",
"SIX": true,
"FOUR": [
"item",
5
],
"TWO": {
"THREE": 4
},
"SEVEN": 1.2
}