如何使用jackson-dataformat-csv解析CSV时跳过一些初始行?

时间:2017-04-24 09:37:12

标签: java csv jackson

我有以下格式的CSV:

New CSV file

header_1,header_2,header_3,header_4
value_1,value_2,value_3,value_4
value_1,value_2,value_3,value_4
value_1,value_2,value_3,value_4

我有以下用于解析此CSV的代码:

    CsvMapper csvMapper = new CsvMapper();
    CsvSchema schema = CsvSchema.emptySchema().withSkipFirstDataRow(true);
    List<CsvModel> rows = new LinkedList<>();
    MappingIterator<CsvModel> iterator = csvMapper
            .readerFor(CsvModel.class).with(schema)
            .readValues(filePath.toFile());
    while (iterator.hasNext()) {
        CsvModel csvElement = iterator.next();
        if (StringUtils.isBlank(csvElement.getValue1())) {
            // skip lines not having the value
            continue;
        }
        rows.add(csvElement);
    }

但是,在解析上述CSV格式文件时,我收到以下异常:

com.fasterxml.jackson.databind.RuntimeJsonMappingException: Can not construct instance of com.adapters.CsvParsing: no String-argument constructor/factory method to deserialize from String value ('')
 at [Source: com.fasterxml.jackson.dataformat.csv.impl.UTF8Reader@2cb566f1; line: 2, column: 1]

这是因为第二行是空的。我需要跳过前两行,如何告诉jackson跳过文件的前两行?

编辑1: 这是CsvModel文件:

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class CsvModel {
    public static final String IdField = "Id";
    public static final String NameField = "Name";
    public static final String GuidField = "Guid";
    public static final String SubscriptionNameField = "Subscription Name";
    public static final String DateField = "Date";
    public static final String CostField = "Cost";

    @JsonProperty(IdField)
    private String Id;
    @JsonProperty(NameField)
    private String Name;
    @JsonProperty(GuidField)
    private String Guid;
    @JsonProperty(SubscriptionNameField)
    private String SubscriptionName;
    @JsonProperty(DateField)
    private String Date;
    private Long epochDate;
    @JsonProperty(CostField)
    private Double Cost;

    public String getId() {
        return this.Id;
    }

    public void setId(String id) {
        this.Id = id;
    }

    public String getName() {
        return this.Name;
    }

    public void setName(String name) {
        this.Name = name;
    }

    public String getGuid() {
        return this.Guid;
    }

    public void setGuid(String guid) {
        this.Guid = guid;
    }

    public String getSubscriptionName() {
        return this.SubscriptionName;
    }

    public void setSubscriptionName(String subscriptionName) {
        this.SubscriptionName = subscriptionName;
    }

    public String getDate() {
        return this.Date;
    }

    public void setDate(String date) {
        this.Date = date;
    }

    public Long getEpochDate() {
        return this.epochDate;
    }

    public void setEpochDate(Long epochDate) {
        this.epochDate = epochDate;
    }

    public Double getCost() {
        return this.Cost;
    }

    public void setCost(Double cost) {
        this.Cost = cost;
    }
}

3 个答案:

答案 0 :(得分:2)

要跳过空行,可以使用SKIP_EMPTY_LINES功能:

CsvMapper csvMapper = new CsvMapper().enable(CsvParser.Feature.SKIP_EMPTY_LINES);

CsvSchema schema = csvMapper.emptySchema().withSkipFirstDataRow(true);
MappingIterator<Account> dataIterator = csvMapper.readerFor(CsvModel.class).with(schema)
    .readValues(file);

答案 1 :(得分:1)

您可以在处理前将迭代器提前两次跳过前两行,例如:

for(int i=0 ; i<2 ; i++){
    if(iterator.hasNext()){
        iterator.next();
    }
}

while (iterator.hasNext()) {
...

如果假设该文件少于2条记录,这将确保不会抛出Exception

<强>更新

根据问题编辑编辑答案:

它抛出RuntimeJsonMappingException的原因是因为它将一行vsc文件解释为字符串并且正在尝试将String绑定到CsvModel对象。您需要使用schema映射器指定(非空)csvMapper,并在反序列化一行时充当metadata,例如:

CsvSchema schema = CsvSchema.builder()
        .setColumnSeparator(',')
        .addColumn("Id")
        .addColumn("name")
        ....

您可以查看this示例。

答案 2 :(得分:0)

一个更好的答案是使用BufferedReader.readLine()消耗前几行。

请参阅此Apache commons csv skip lines

示例代码:

try (final BufferedReader reader = new BufferedReader(new FileReader(csvFile))) {
  // consume the first few lines here
  reader.readLine();
  reader.readLine();

  final MappingIterator<MyClass> readValues =
    new CsvMapper()
         .readerFor(MyClass.class)
         .with(emptySchema()
                .withHeader()
                .withNullValue(""))
         .readValues(reader);

  final List<MyClass> records = readValues.readAll();
} catch (Exception e) {
  log.warn("Failed to read detail section of transactionItem file.");
}