复杂JSON数据的扁平化解析_Java实现

需求:

将复杂JSON数据解构为扁平化数据,插入数据库对应的一张表上,供业务更直观地查询/阅览数据。

思路:

使用Jackson将数据转成JsonNode对象,然后通过递归解构至JsonNode不在为对象为止,然后put到一个LinkedHashMap中去,以此实现数据结构的扁平化变换。

 

需要的Jackson依赖(如果你的项目中有spring-boot则无需引入该maven依赖)

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.14.2</version>
        </dependency>

 

核心代码:

public class Main {

    public static final ClassLoader CLASS_LOADER = Thread.currentThread().getContextClassLoader();
    public static final ObjectMapper objectMapper = new ObjectMapper();
    public static final int MAX_DEPTH = 100;

    public static void main(String[] args) throws IOException {
        String str = readFile("p.json");
        JsonNode jsonNode = objectMapper.readTree(str);
        Map<String, String> map = recursiveParse(new LinkedHashMap<>(), jsonNode, null, 1);
        map.forEach((k, v) -> System.out.println(k + ":" + v));
    }

    /**
     * @param map      以字段名为key,字段值为value的map
     * @param jsonNode JSON数据转JsonNode后的对象
     * @param pKey     父级字段名
     * @param depth    递归深度标识
     */
    public static Map<String, String> recursiveParse(Map<String, String> map, JsonNode jsonNode, String pKey, int depth) {
        if (depth > MAX_DEPTH) {
            // 避免无限递归
            System.out.println("超出递归深度阈值,结束当前递归方法");
            return map;
        }
        if (jsonNode.isObject()) {
            if (depth != 1) {
                map.put(pKey, jsonNode.toString());
            }
            jsonNode.fields().forEachRemaining(e -> {
                // 给字段名加上json层级标识,可根据需求去除或改动
                String cKey = depth + "_" + e.getKey();
                JsonNode cJsonNode = e.getValue();
                recursiveParse(map, cJsonNode, cKey, depth + 1);
            });
        } else if (jsonNode.isArray()) {
            // 此前的经验:当JSON数据过于复杂时或Array的元素个数非常多时,Array结构若继续解析会导致一张表的字段数量过千
            map.put(pKey, jsonNode.toString()); // 因此数组类case额外处理
        } else if (jsonNode.isValueNode()) {
            map.put(pKey, jsonNode.toString());
        }
        return map;
    }

    public static String readFile(String fileName) {
        String result;
        try (InputStream inputStream = CLASS_LOADER.getResourceAsStream(fileName)) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream), StandardCharsets.UTF_8));
            result = reader.lines().collect(Collectors.joining(System.lineSeparator()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }
}

 

模拟一个多层级JSON数据测试:

{
  "customer_id": "007",
  "customer_info": {
    "name": "Taco",
    "house": {
      "area": 100.1,
      "price": 20000,
      "currency": "USD"
    }
  },
  "customer_group": "牛马"
}

 

测试结果

1_customer_id:"007"
1_customer_info:{"name":"Taco","house":{"area":100.1,"price":20000,"currency":"USD"}}
2_name:"Taco"
2_house:{"area":100.1,"price":20000,"currency":"USD"}
3_area:100.1
3_price:20000
3_currency:"USD"
1_customer_group:"牛马"

 一般情况下,数据库表字段名不能以数字开头,因此字段名如果需要显式标识层级,key建议加上一个j,例如j2_name

 

现在,你可以根据字段名组建建表语句,并根据map的value值组建插入语句了。

posted @ 2024-01-22 16:08  Ashe|||^_^  阅读(179)  评论(2编辑  收藏  举报