将数组的JSON数组转换为对象映射(Java)

时间:2020-09-12 13:44:38

标签: java arrays json jackson

我有使用Java,Apache POI和Jackson从Excel文件中提取到JSON结构中的数据。生成的JSON数据结构如下所示:

{
  "fileName" : "C:\\Users\\jgagnon\\sample_data\\PDM_BOM.xlsx",
  "sheets" : [ {
    "name" : "PDM_BOM",
    "data" : [ [ "BRANCH", "PARENT ITEM NUMBER", "2ND ITEM NUMBER", "QUANTITY REQUIRED", "UNIT OF MEASURE", "ISSUE TYPE CODE", "LINE TYPE", "STOCK TYPE", "TYPE BOM", "LINE NUMBER", "OPERATING SEQUENCE", "EFFECTIVE FROM DATE", "EFFECTIVE THRU DATE", "DRAWING NUMBER", "UNIT COST", "SCRAP PERCENT" ],
        [ "B20", "208E8840040", "5P884LPFSR2", 0.32031, "LB", "I", "S", "M", "M", 1.0, 10.0, "09/11/13", "12/31/40", null, 0.0, 0.0 ],
        [ "B20", "208E8840168", "5P884LPFSR2", 1.36, "LB", "I", "S", "M", "M", 1.0, 10.0, "02/26/08", "12/31/40", null, 0.0, 0.0 ],
        [ "B20", "208E8840172", "5P884LPFSR2", 1.3924, "LB", "I", "S", "M", "M", 1.0, 10.0, "02/26/08", "12/31/40", null, 0.0, 0.0 ],
        [ "B20", "208E8840180", "5P884LPFSR2", 1.4565, "LB", "I", "S", "P", "M", 1.0, 10.0, "03/04/09", "12/31/40", null, 0.0, 0.0 ],
        [ "B20", "21PPH150166", "8P315TPMRG", 1.39629, "LB", "I", "S", "M", "M", 1.0, 10.0, "03/05/18", "12/31/40", null, 0.0, 0.0 ] ],
    "maxCols" : 16,
    "maxRows" : 14996
  } ]
}

data元素中,本质上是一个数组数组,代表工作表中的所有行。第一个行数组是随后数据行的列标题。

我希望能够重组data,使其以对象映射的形式出现,其中每个键(在此示例中为 )是PARENT ITEM NUMBER列。与该键关联的映射值将是一个JSON对象,该对象由键/值对,列标题和当前数据行的列值组成。

因此,使用上面的示例,我最终得到这样的结果: (我的JSON语法/结构可能不正确)

{
  "data": {
    "208E8840040": {
      "BRANCH": "B20",
      "PARENT ITEM NUMBER": "208E8840040",
      "2ND ITEM NUMBER": "5P884LPFSR2",
      "QUANTITY REQUIRED": 0.32031,
      "UNIT OF MEASURE": "LB",
      "ISSUE TYPE CODE": "I",
      "LINE TYPE": "S",
      "STOCK TYPE": "M",
      "TYPE BOM": "M", 
      "LINE NUMBER": 1.0, 
      "OPERATING SEQUENCE": 10.0, 
      "EFFECTIVE FROM DATE": "09/11/13", 
      "EFFECTIVE THRU DATE": "12/31/40", 
      "DRAWING NUMBER": null, 
      "UNIT COST": 0.0, 
      "SCRAP PERCENT": 0.0
    },
    "208E8840168": {
      "BRANCH": "B20",
      "PARENT ITEM NUMBER": "208E8840168",
      "2ND ITEM NUMBER": "5P884LPFSR2",
      "QUANTITY REQUIRED": 1.36,
      ...
    },
    ...
  }
}

我正在寻找一种将前者转换成后者的方法。

更新

我刚刚意识到我遗漏了一个重要细节。

此工作表(表)中的数据基本上由PARENT ITEM NUMBER列键入。但是,尽管该列是主要标识符,但在表中并不总是唯一的。

在很多情况下,PARENT ITEM NUMBER有多行具有相同的值。这些行中的每一行都包含有关组成“父级”的元素的信息(将其视为子项)。这些子项由2ND ITEM NUMBER列标识。

此外,许多子项将在表中有自己的行,其中PARENT ITEM NUMBER2ND ITEM NUMBER为父项标识的子项编号。您可能已经猜到过,这些子项可以有自己的子项,依此类推。

基本上,这是相关数据的多个层次的表格表示。一些子项目将出现(重复使用)多个父项目。

我不知道这会使我想做的事情复杂化。

更新

感谢https://stackoverflow.com/users/51591/micha%c5%82-ziober的初衷。我对其进行了少许修改,以生成子项列表的地图。修改后的代码如下:

  public String convertToJson(File jsonFile) throws IOException {
    ArrayNode arrayNode = readDataArray(jsonFile);

    List<Map<String, JsonNode>> rowMaps = convertArrayToMaps(arrayNode);

    Map<Object, List<Map<String, JsonNode>>> dataMap = rowMaps.stream()
        .collect(Collectors.groupingBy(map -> map.get("PARENT ITEM NUMBER").textValue()));
    return jsonMapper.writeValueAsString(Collections.singletonMap("data", dataMap));
  }

这是输出示例:

{
  "data" : {
    "MTDMN97PJ1A9" : [ {    <- 1 child
      "BRANCH" : "B70",
      "PARENT ITEM NUMBER" : "MTDMN97PJ1A9",
      "2ND ITEM NUMBER" : "MTDMN970144XO",
      "QUANTITY REQUIRED" : 12.0,
      "UNIT OF MEASURE" : "SY",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 1.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "01/18/19",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    } ],
    "ZCP723A1152" : [ {     <- 4 children
      "BRANCH" : "B70",
      "PARENT ITEM NUMBER" : "ZCP723A1152",
      "2ND ITEM NUMBER" : "5P587UMFSD2",
      "QUANTITY REQUIRED" : 2.32222,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 3.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "05/15/17",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    }, {
      "BRANCH" : "B70",
      "PARENT ITEM NUMBER" : "ZCP723A1152",
      "2ND ITEM NUMBER" : "8P550ZPPOOLE",
      "QUANTITY REQUIRED" : 2.32222,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 1.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "05/15/17",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    }, {
      "BRANCH" : "B70",
      "PARENT ITEM NUMBER" : "ZCP723A1152",
      "2ND ITEM NUMBER" : "8P906WPPA3077",
      "QUANTITY REQUIRED" : 4.64444,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 2.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "05/15/17",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    }, {
      "BRANCH" : "B70",
      "PARENT ITEM NUMBER" : "ZCP723A1152",
      "2ND ITEM NUMBER" : "8U910LKSHBL",
      "QUANTITY REQUIRED" : 2.32222,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 4.01,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "12/13/17",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    } ],
    ... many more entries
}

1 个答案:

答案 0 :(得分:1)

您可以将整个JSON有效负载读取为JsonNode,并检索data属性,它是一个数组。如果我们可以假设第一个元素始终是名称数组,而所有其他元素的大小均相等,则可以手动创建Map并将其序列化回JSON。一个棘手的部分是如何为结果JSON生成唯一的键值。示例代码如下所示:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Excel2JonsApp {
    public static void main(String[] args) throws IOException {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Excel2JsonMapper mapper = new Excel2JsonMapper();
        String json = mapper.convertToJson(jsonFile);
        System.out.println(json);
    }
}

class Excel2JsonMapper {
    private final ObjectMapper jsonMapper;

    Excel2JsonMapper() {
        this.jsonMapper = createJsonMapper();
    }

    private ObjectMapper createJsonMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        return mapper;
    }

    String convertToJson(File jsonFile) throws IOException {
        ArrayNode arrayNode = readDataArray(jsonFile);
        Map<Object, Map<String, JsonNode>> dataMap = convertArrayToMaps(arrayNode)
                .stream()
                .collect(Collectors.toMap(
                        map -> map.get("PARENT ITEM NUMBER").textValue(), //key generator function
                        Function.identity()));

        return jsonMapper.writeValueAsString(Collections.singletonMap("data", dataMap));
    }

    private List<Map<String, JsonNode>> convertArrayToMaps(ArrayNode arrayNode) {
        ArrayNode names = (ArrayNode) arrayNode.get(0);

        Iterator<JsonNode> iterator = arrayNode.iterator();
        iterator.next();// skip names

        List<Map<String, JsonNode>> list = new ArrayList<>();
        while (iterator.hasNext()) {
            ArrayNode values = (ArrayNode) iterator.next();
            Map<String, JsonNode> map = new LinkedHashMap<>();
            for (int i = 0; i < names.size(); i++) {
                map.put(names.get(i).textValue(), values.get(i));
            }
            list.add(map);
        }
        return list;
    }

    private ArrayNode readDataArray(File jsonFile) throws IOException {
        JsonNode root = jsonMapper.readTree(jsonFile);
        return (ArrayNode) root.at("/sheets/0/data");
    }
}

上面的代码打印:

{
  "data" : {
    "208E8840168" : {
      "BRANCH" : "B20",
      "PARENT ITEM NUMBER" : "208E8840168",
      "2ND ITEM NUMBER" : "5P884LPFSR2",
      "QUANTITY REQUIRED" : 1.36,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 1.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "02/26/08",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    },
    "21PPH150166" : {
      "BRANCH" : "B20",
      "PARENT ITEM NUMBER" : "21PPH150166",
      "2ND ITEM NUMBER" : "8P315TPMRG",
      "QUANTITY REQUIRED" : 1.39629,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 1.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "03/05/18",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    },
    "208E8840180" : {
      "BRANCH" : "B20",
      "PARENT ITEM NUMBER" : "208E8840180",
      "2ND ITEM NUMBER" : "5P884LPFSR2",
      "QUANTITY REQUIRED" : 1.4565,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "P",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 1.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "03/04/09",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    },
    "208E8840172" : {
      "BRANCH" : "B20",
      "PARENT ITEM NUMBER" : "208E8840172",
      "2ND ITEM NUMBER" : "5P884LPFSR2",
      "QUANTITY REQUIRED" : 1.3924,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 1.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "02/26/08",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    },
    "208E8840040" : {
      "BRANCH" : "B20",
      "PARENT ITEM NUMBER" : "208E8840040",
      "2ND ITEM NUMBER" : "5P884LPFSR2",
      "QUANTITY REQUIRED" : 0.32031,
      "UNIT OF MEASURE" : "LB",
      "ISSUE TYPE CODE" : "I",
      "LINE TYPE" : "S",
      "STOCK TYPE" : "M",
      "TYPE BOM" : "M",
      "LINE NUMBER" : 1.0,
      "OPERATING SEQUENCE" : 10.0,
      "EFFECTIVE FROM DATE" : "09/11/13",
      "EFFECTIVE THRU DATE" : "12/31/40",
      "DRAWING NUMBER" : null,
      "UNIT COST" : 0.0,
      "SCRAP PERCENT" : 0.0
    }
  }
}