【问题标题】:Flatten a JSON string to make the key including each level key value to Map<String, String> using Gson or Jackson使用 Gson 或 Jackson 展平 JSON 字符串以使包含每个级别键值的键映射到 Map<String, String>
【发布时间】:2017-07-10 18:18:46
【问题描述】:

我有一个关于Flatten a JSON string to Map using Gson or Jackson 的增强问题。

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

那么如何实现呢?

例如:

{
    "id" : "123",
    "name" : "Tom",
    "class" : {
        "subject" : "Math",
        "teacher" : "Jack"
    }
}

我要获取地图:

"id" : "123",
"name" : "Tom",
"class.subject" : "Math",
"class.teacher" : "Jack"

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

根据@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)));
    }
}

【问题讨论】:

  • 我认为您可能必须自己简单地递归(或迭代)进行转换。
  • 实际上我发现了另一个能够迭代 json 字符串并正确打印值的问题。但是如何添加/保存密钥?我无法弄清楚。你能帮忙吗? stackoverflow.com/questions/22111857/…

标签: java json jackson gson


【解决方案1】:

您可以将 JSON 作为 JsonNode 并递归遍历所有字段并将键和值字段添加到 Map。当值是对象而不是字符串时,您可以将字段名称添加到 List 以在最终遇到字符串时与句点连接。首先创建(为了便于阅读)一个单独的方法,将 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)));
    }
}

并像这样使用它:

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

fields() 返回 Iterator。当心 StackOverflowErrors 以及深度嵌套 JSON 的低性能。

【讨论】:

  • 非常感谢尼克。我能够根据您的回答找到解决方案,并详细更新了我的问题。
【解决方案2】:

我使用下面的简单代码解决了这个问题,只认为需要下载 jettison 和 flattener.JsonFlattener 库

import java.util.Map;
import org.codehaus.jettison.json.JSONObject;
import com.github.wnameless.json.flattener.JsonFlattener;

public class test {

    public static void main(String[] args) {
        String jsonString = "{\"id\" : \"123\",\"name\" : \"Tom\",\"class\" : {\"subject\" : \"Math\",\"teacher\" : \"Jack\"}}";
        JSONObject jsonObject = new JSONObject();
        String flattenedJson = JsonFlattener.flatten(jsonString);
        Map<String, Object> flattenedJsonMap = JsonFlattener.flattenAsMap(jsonString);
        System.out.println(flattenedJsonMap);
    }
}

参考链接:https://github.com/wnameless/json-flattener

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2011-10-20
    • 2018-12-07
    相关资源
    最近更新 更多