使用 Gson 或 Jackson 压平 JSON 字符串,将包含各个级别键值的键制作为 Map<String, String>

Laz*_*ang 5 java json jackson gson

我有一个关于使用 Gson 或 Jackson 将 JSON 字符串展平为 Map 的增强问题。

我的场景包含重复的键,因此上述问题中的解决方案会导致一些重复的键被覆盖。所以我正在考虑通过将每个级别的密钥组合在一起来构造密钥。

那么如何实现呢?

例如:

{
    "id" : "123",
    "name" : "Tom",
    "class" : {
        "subject" : "Math",
        "teacher" : "Jack"
    }
}
Run Code Online (Sandbox Code Playgroud)

我想获取地图:

"id" : "123",
"name" : "Tom",
"class.subject" : "Math",
"class.teacher" : "Jack"
Run Code Online (Sandbox Code Playgroud)

**********************更新解决方案************************ **************

根据@Manos Nikolaidis的回答,我能够通过考虑ArrayNode来实现以下解决方案。

public void processJsonString(String jsonString) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    ArrayNode arrayNode = (ArrayNode) mapper.readTree(jsonString);
    processArrayNode(arrayNode);
}

private void processObjectNode(JsonNode jsonNode) {
    Map<String, String> result = new HashMap<>();
    Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
    iterator.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
}

private void processArrayNode(ArrayNode arrayNode) {
    for (JsonNode jsonNode : arrayNode) {
        processObjectNode(jsonNode);
    }
}


private void mapAppender(Map<String, String> result, Map.Entry<String, JsonNode> node, List<String> names) {
    names.add(node.getKey());
    if (node.getValue().isTextual()) {
        String name = names.stream().collect(Collectors.joining("."));
        result.put(name, node.getValue().asText());
    } else if (node.getValue().isArray()) {
        processArrayNode((ArrayNode) node.getValue());
    } else if (node.getValue().isNull()) {
        String name = names.stream().collect(Collectors.joining("."));
        result.put(name, null);
    } else {
        node.getValue().fields()
                        .forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
    }
}
Run Code Online (Sandbox Code Playgroud)

Man*_*dis 1

您可以获取 JSON 并JsonNode递归遍历所有字段,并将键和值字段添加到 Map。当值是对象而不是字符串时,您可以将字段名称添加到列表中,以便在最终遇到字符串时与句点连接。首先创建(为了可读性)一个单独的方法,将 Json 字段添加到Map

void mapAppender(Map<String, String> result, Entry<String, JsonNode> node, List<String> names) {
    names.add(node.getKey());
    if (node.getValue().isTextual()) {
        String name = names.stream().collect(joining("."));
        result.put(name, node.getValue().asText());
    } else {
        node.getValue().fields()
            .forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

ObjectMapper mapper = new ObjectMapper();
Map<String, String> result = new HashMap<>();
mapper.readTree(json).fields()
    .forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
Run Code Online (Sandbox Code Playgroud)

其中fields()返回一个Iterator. 请注意StackOverflowErrors深度嵌套的 JSON,其性能可能较低。