将JSON数组转换为特定类的Java列表

huangapple go评论110阅读模式
英文:

Convert JSON array of arrays to Java List of a specific class

问题

假设我有从源提取的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元素中,它是一个表示工作表行的数组数组。第一行包含列标题。

现在,假设我有一个Java类BOM.java,如下所示:

public class BOM {

  private String branch;
  private String parentItemNumber;
  private String secondItemNumber;
  private double quantityRequired;
  private String unitOfMeasure;
  private String issueTypeCode;
  private String lineType;
  private String stockType;
  private String typeBom;
  private double lineNumber;
  private double operatingSequence;
  private String effectiveFromDate;
  private String effectiveThruDate;
  private String drawingNumber;
  private double unitCost;
  private double scrapPercent;

  // getters/setters omitted
}

是否有一种方法可以解析此JSON数据,以便得到BOM对象的java.util.List

我至少认为,我需要在上面的类中添加一些Jackson的“JSON”注解,以帮助Jackson进行字段映射?

到目前为止,我使用了Jackson的ObjectMapper.readTree(File)方法来导入数据。这提供了一个JsonNode层次结构,我可以通过导航到其中的项。

我找到了一些建议,涉及使用Jackson的TypeFactoryTypeReference,但我不理解它们的工作原理,无法使它们对我起作用。

这是我目前正在使用的简单程序:

import java.io.File;
import java.io.IOException;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JSONObjectReader {

  public static void main(String[] args)
      throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();
    JsonNode root = mapper.readTree(new File("PDM_BOM.json"));
    JsonNode sheets = root.get("sheets");
    JsonNode sheet1 = sheets.get(0);
    JsonNode dataNode = sheet1.get("data");

    // skip first row
    for (int i = 1; i < dataNode.size(); i++) {
      JsonNode n = dataNode.get(i);
      System.out.println(n);
    }
  }

}
英文:

Let's assume I have JSON data that has been extracted from a source in the following form:

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

The information I care about is in the data element, which is an array of arrays representing the rows from a sheet. The first row contains the column headers.

Now, assume I have a Java class, BOM.java as follows:

public class BOM {

  private String branch;
  private String parentItemNumber;
  private String secondItemNumber;
  private double quantityRequired;
  private String unitOfMeasure;
  private String issueTypeCode;
  private String lineType;
  private String stockType;
  private String typeBom;
  private double lineNumber;
  private double operatingSequence;
  private String effectiveFromDate;
  private String effectiveThruDate;
  private String drawingNumber;
  private double unitCost;
  private double scrapPercent;

  // getters/setters omitted
}

Is there a way to parse this JSON data that results in a java.util.List of BOM objects?

I assume at the very least, that I need to add some Jackson "JSON" annotations to the class above to help Jackson with field mapping?

Up to this point I have used the Jackson ObjectMapper.readTree(File) method to pull in the data. This provides a JsonNode hierarchy which I can navigate to get to items.

I found some suggestions that referred to using a Jackson TypeFactory or TypeReference, but I don't understand how they work and couldn't get them to work for me.

Here is the simple program I'm currently using:

import java.io.File;
import java.io.IOException;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JSONObjectReader {

  public static void main(String[] args)
      throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();
    JsonNode root = mapper.readTree(new File(&quot;PDM_BOM.json&quot;));
    JsonNode sheets = root.get(&quot;sheets&quot;);
    JsonNode sheet1 = sheets.get(0);
    JsonNode dataNode = sheet1.get(&quot;data&quot;);

    // skip first row
    for (int i = 1; i &lt; dataNode.size(); i++) {
      JsonNode n = dataNode.get(i);
      System.out.println(n);
    }
  }

}

答案1

得分: 1

你的列表结构几乎类似于CSV格式:一行列名,后面是许多行的值。经过搜索,我找不到任何Jackson内置的方法来处理这种结构,甚至没有一个方法可以解析列表并忽略第一个元素。

我认为你应该忽略第一个元素的原因是,如果你的目标是解析成一个POJO,而不是例如Map<String, String>,那么处理它将会非常困难。这是因为如果第一行不固定,字段名称可能会更改,那么你的POJO将不再反映你正在解析的内容。最好只忽略标题,或者如果标题与预期不符,则抛出异常,专注于解析后续的数组。

我会这样做,编写一个自定义反序列化器,将List<List<String>>转换为List<BOM>。反序列化器应该:

  1. 获取原始的列表列表
  2. 抛弃(或验证)第一个条目
  3. 对于所有后续条目,将它们转换为BOM
    1. 我会编写一个接受List<String>BOM构造函数来实现这一点,但你也可以在反序列化器中完成所有的转换。
英文:

Your list structure is almost like a CSV: a row of column names, followed by many rows of values. Looking around, I couldn't find any Jackson built-ins to handle such a structure - not even one to parse a list while ignoring the first element.

The reason I think you should ignore the first element is that it will be very hard to deal with if your goal is to parse into a POJO and not, for example, a Map&lt;String, String&gt;. This is because if the first row is not fixed, your field names may change, and then your POJO will no longer reflect what you're parsing. Better to just ignore the header, or throw an exception if it doesn't match what you expect, and focus on parsing the subsequent arrays.

The way I would do this is to write a Custom Deserializer for turning List&lt;List&lt;String&gt;&gt; into List&lt;BOM&gt;. The deserializer should:

  1. Get the raw list of lists
  2. Throw out (or verify) the first entry
  3. For all subsequent entries, convert them to BOM
    1. I would do this by writing a BOM constructor that takes a List&lt;String&gt;, but you could also do all the conversion in the deserializer.

答案2

得分: 1

应该 可以工作:

List<DTO> result = new ArrayList<>();

for (int i = 1; i < dataNode.size(); i++) {
  JsonNode n = dataNode.get(i);
  result.add(mapper.readValue(mapper.writeValueAsString(n), DTO.class));
}

注意这实际上相当丑陋 - 它解析了所有节点然后将其写回JSON字符串最后重新解析为DTO更清晰的版本将需要处理格式不正确的第一行
英文:

This should work:

List&lt;DTO&gt; result = new ArrayList&lt;&gt;();

for (int i = 1; i &lt; dataNode.size(); i++) {
  JsonNode n = dataNode.get(i);
  result.add(mapper.readValue(mapper.writeValueAsString(n), DTO.class));
}

Note that it is really ugly - it parses all nodes and then writes back to a JSON string before finally re-parsing as a DTO. A cleaner version would require dealing with the malformed 1st row.

huangapple
  • 本文由 发表于 2020年9月15日 03:03:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/63890415.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定