Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typo3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在嵌套贴图中使用Jackson KeyDeserializer? 问题_Java_Json_Jackson - Fatal编程技术网

Java 如何在嵌套贴图中使用Jackson KeyDeserializer? 问题

Java 如何在嵌套贴图中使用Jackson KeyDeserializer? 问题,java,json,jackson,Java,Json,Jackson,在现有的应用程序中,我必须反序列化可能的深度嵌套映射,并在所有级别的所有键上应用自定义Jackson反序列化程序 由于应用程序动态处理各种数据模型,我无法使用带有明确类型映射的显式数据模型。。。相反,我使用映射并检查反序列化的对象值本身是否为映射 这会导致自定义反序列化程序仅应用于顶级映射 更新:需要注意的是,我不能将反序列化程序绑定到所有映射,因为我的数据模型通常也有更具体的映射。对于开放式JSON设置,我通常有一个通用的Map字段;与例如Map一起生活,以获得更明确的配置位 例子 为了举例

在现有的应用程序中,我必须反序列化可能的深度嵌套映射,并在所有级别的所有键上应用自定义Jackson反序列化程序

由于应用程序动态处理各种数据模型,我无法使用带有明确类型映射的显式数据模型。。。相反,我使用
映射
并检查反序列化的
对象
值本身是否为
映射

这会导致自定义反序列化程序仅应用于顶级映射

更新:需要注意的是,我不能将反序列化程序绑定到所有映射,因为我的数据模型通常也有更具体的映射。对于开放式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
}