复杂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值组建插入语句了。