背景

公司的中台产品,需要对外部API接口返回的JSON数据进行采集入湖,有时候外部API接口返回的JSON数据层级嵌套比较深,举个栗子:

上述的JSON数据中,最外层为请求返回对象,data里面包含返回的业务数据,业务数据按照学校 / 班级 / 学生进行嵌套

在数据入湖时,需要按照最内层的学生视角将数据拆分为行列数据,最终的拆分结果如下:

由于对接的外部API接口返回的JSON数据结构不是统一的、固定的,所以需要通过一种算法对每一层对象、数组进行遍历和钻取,实现JSON数据的扁平化

网上找了一些JSON扁平化的中间件,例如:Json2Flat在扁平化处理过程不太完美,不支持跨层级的数组嵌套结构

所以决定自己实现扁平化处理

关键代码如下:

public class LinkedNode {

    private LinkedNode parent;

    private String parentName;

    private Map<String, Object> data;

    public LinkedNode(LinkedNode parent, String parentName, Map<String, Object> data) {
this.parent = parent;
this.parentName = parentName;
this.data = data;
}
}
public class JSONFlatProcessor {

    private LinkedList<LinkedNode> nodes;

    private LinkedList<String> column;

    private List<Object[]> data;

    public void find(LinkedNode parent, String parentName, Map<String, Object> data) {
LinkedNode node = new LinkedNode(parent, parentName, data);
if (!hasObjectOrArray(data)) {
nodes.add(node);
} else {
for (Map.Entry entry : data.entrySet()) {
if (entry.getValue() instanceof Map) {
find(node, String.valueOf(entry.getKey()), (Map<String, Object>) entry.getValue());
} else if (isObjectArray(entry.getValue())) {
find(node, String.valueOf(entry.getKey()), (List<Map<String, Object>>) entry.getValue());
}
}
}
} public void find(LinkedNode parent, String parentName, List<Map<String, Object>> data) {
for (Map<String, Object> item : data) {
find(parent, parentName, item);
}
} protected Boolean hasObjectOrArray(Map<String, Object> item) {
Object field;
for (Map.Entry entry : item.entrySet()) {
field = entry.getValue();
if (field instanceof Map || isObjectArray(field)) {
return Boolean.TRUE;
}
} return Boolean.FALSE;
} protected Boolean isObjectArray(Object object) {
return object instanceof List
&& !CollectionUtils.isEmpty((List) object)
&& ((List) object).get(0) instanceof Map;
} public JSONFlatProcessor process(List<Map<String, Object>> data) {
nodes = new LinkedList<>();
find(null, null, data);
return this;
} public JSONFlatProcessor process(Map<String, Object> data) {
nodes = new LinkedList<>();
find(null, null, data);
return this;
} public LinkedList<LinkedNode> getNodes() {
return nodes;
} public List<String> getColumn() { if (CollectionUtils.isEmpty(nodes)) {
return Collections.emptyList();
} column = new LinkedList<>();
collectColumn(nodes.getFirst()); return column; } protected void collectColumn(LinkedNode node) {
List<String> innerColumn = new ArrayList<>(node.getData().size());
String columnBuilder;
for (Map.Entry entry : node.getData().entrySet()) {
if (!(entry.getValue() instanceof Map || isObjectArray(entry.getValue()))) {
columnBuilder = null == node.getParentName()? String.valueOf(entry.getKey()) : String.format("%s.%s", node.getParentName(), entry.getKey());
innerColumn.add(columnBuilder);
}
}
column.addAll(0, innerColumn); if (null != node.getParent()) {
collectColumn(node.getParent());
}
} public List<Object[]> getData() { if (CollectionUtils.isEmpty(nodes)) {
return Collections.emptyList();
} data = new ArrayList<>(nodes.size()); LinkedList<Object> container; for (LinkedNode node : nodes) {
container = new LinkedList<>();
collectData(node, container);
data.add(container.toArray());
} return data; } protected void collectData(LinkedNode node, LinkedList<Object> container) {
List<Object> innerData = new ArrayList<>(node.getData().size());
for (Map.Entry entry : node.getData().entrySet()) {
if (!(entry.getValue() instanceof Map || isObjectArray(entry.getValue()))) {
innerData.add(entry.getValue());
}
}
container.addAll(0, innerData); if (null != node.getParent()) {
collectData(node.getParent(), container);
}
} protected static class CollectionUtils {
public static boolean isEmpty(Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
} }
public class MainTests {

public static void main(String[] args) throws Exception {
String jsonStr = "{\"code\":200,\"requestId\":\"1680177848458\",\"data\":[{\"school\":\"xxx市第一实验小学\",\"no\":\"1001\",\"class\":[{\"name\":\"一(1)班\",\"teacher\":\"吴老师\",\"student\":[{\"name\":\"张同学\",\"age\":6},{\"name\":\"王同学\",\"age\":7}]}]},{\"school\":\"xxx市第二实验小学\",\"no\":\"1002\",\"class\":[{\"name\":\"一(2)班\",\"teacher\":\"陈老师\",\"student\":[{\"name\":\"欧阳同学\",\"age\":6}]}]}]}";
ObjectMapper jsonMapper = new ObjectMapper();
// List<Map<String, Object>> map = jsonMapper.readValue(jsonStr, List.class);
Map<String, Object> map = jsonMapper.readValue(jsonStr, Map.class);

JSONFlatProcessor processor = new JSONFlatProcessor().process(map);
System.out.println("数据条数: " + processor.getNodes().size());
System.out.println("字段名: " + processor.getColumn());
System.out.println("首行数据: " + new ObjectMapper().writeValueAsString(processor.getData().get(0)));
}

}
数据条数: 3
字段名: [code, requestId, data.school, data.no, class.name, class.teacher, student.name, student.age]
首行数据: [200,"1680177848458","xxx市第一实验小学","1001","一(1)班","吴老师","张同学",6]

JSON多层嵌套复杂结构数据扁平化处理转为行列数据的更多相关文章

  1. 【SpringBoot】 Java中如何封装Http请求,以及JSON多层嵌套解析

    前言 本文中的内容其实严格来说不算springboot里面的特性,属于JAVA基础,只是我在项目中遇到了,特归纳总结一下. HTTP请求封装 目前JAVA对于HTTP封装主要有三种方式: 1. JAV ...

  2. Json多层嵌套,要怎么提取?

    一直用Jmeter的Json Extactor,对于多层的Json嵌套,很好用,自己写代码的时候,总是遇到各种Exception 看了网上的资料,整理一下 1. 最简单的JSON提取,只有一层的时候 ...

  3. mybatis 注解写法 多层嵌套foreach,调用存储过程,批量插入数据

    @Select("<script>" + "DECLARE @edi_Invoice_Details edi_Invoice_Details;" + ...

  4. 【JS简洁之道小技巧】第一期 扁平化数组

    介绍两种方法,一是ES6的flat,简单粗暴.二是递归,也不麻烦. flat ES6自带了flat方法,用于使一个嵌套的数组扁平化,默认展开一个嵌套层.flat方法接收一个数字类型参数,参数值即嵌套层 ...

  5. ASP.NET提取多层嵌套json数据的方法

    本文实例讲述了ASP.NET利用第三方类库Newtonsoft.Json提取多层嵌套json数据的方法,具体例子如下. 假设需要提取的json字符串如下: {"name":&quo ...

  6. .net(c#)提取多层嵌套的JSON

    Newtonsoft.Json.Net20.dll 下载请访问http://files.cnblogs.com/hualei/Newtonsoft.Json.Net20.rar 在.net 2.0中提 ...

  7. [转]easyui tree 模仿ztree 使用扁平化加载json

    原文地址:http://my.oschina.net/acitiviti/blog/349377 参考文章:http://www.jeasyuicn.com/demo/treeloadfilter.h ...

  8. c#多层嵌套Json

    Newtonsoft.Json.Net20.dll 下载请访问http://files.cnblogs.com/hualei/Newtonsoft.Json.Net20.rar 在.net 2.0中提 ...

  9. 提取多层嵌套Json数据

    在.net 2.0中提取这样的json {"name":"lily","age":23,"addr":{"ci ...

  10. 多层嵌套的json数据

    很多时候我们见到的json数据都是多层嵌套的,就像下面这般: {"name":"桔子桑", "sex":"男", , & ...

随机推荐

  1. JDK中动态库加载路径问题,一文讲清

    前言 本周协助测试同事对一套测试环境进行扩容,我们扩容很原始,就是新申请一台机器,直接把jdk.resin容器(一款servlet容器).容器中web应用所在的目录,全拷贝到新机器上,servlet容 ...

  2. Unity的AssetPostprocessor之Model之动画:深入解析与实用案例 3

    Unity AssetPostprocessor的Model的动画相关的函数修改实际应用 在Unity中,AssetPostprocessor是一个非常有用的工具,它可以在导入资源时自动执行一些操作. ...

  3. 7.1 C++ STL 非变易查找算法

    C++ STL 中的非变易算法(Non-modifying Algorithms)是指那些不会修改容器内容的算法,是C++提供的一组模板函数,该系列函数不会修改原序列中的数据,而是对数据进行处理.查找 ...

  4. ETL之apache hop系列1-ETL概念与hop简介

    ETL 简单介绍 ETL概念 ETL,是英文Extract-Transform-Load的缩写,用来描述将数据从来源端经过抽取(extract).转换(transform).加载(load)至目的端的 ...

  5. (洛谷P4213)杜教筛

    https://www.cnblogs.com/Mychael/p/8744633.html #pragma GCC optimize(3, "Ofast", "inli ...

  6. 使用GPU搭建支持玛雅(Maya)和Adobe AI,DW,PS的职校云计算机房

    背景 学校为职业学校,计算机教室需要进行Maya.Adobe Illustrator.Adobe Dreamweaver.Adobe PhotoShop等软件的教学.每个教室为35用户.资源需求为4核 ...

  7. 【matplotlib基础】--文本标注

    Matplotlib 文本和标注可以为数据和图形之间提供额外的信息,帮助观察者更好地理解数据和图形的含义. 文本用于在图形中添加注释或提供更详细的信息,以帮助观察者理解图形的含义.标注则是一种更加细粒 ...

  8. KRPano动态热点专用素材图50多个,加动态热点使用方法

    KRPano动态热点专用素材是一种特定形式的序列图,该序列图要求帧的水平和垂直的具体位置必须准确,否则图的动作将会出现错乱,KRPano不支持动态图.目前网上比较匮乏动态热点素材,在此亲手整理制作了5 ...

  9. Skynet通讯遇到的奇怪问题

    问题 最近在做一个内部通讯的服务器, 用的自带的gateserver和socketchannel做通讯, 在使用skynet.unpack或者string.unpack("XXXX" ...

  10. 【开源】给ChatGLM写个,Java对接的SDK

    作者:小傅哥 - 百度搜 小傅哥bugstack 博客:bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 大家好,我是技术UP主小傅哥. 清华大学计算机系的超大规模训练模型 Cha ...