jobin
jobin

Reputation: 2798

How to skip a few initial rows while parsing CSV using jackson-dataformat-csv?

I have a CSV of the following format:

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

I have the following code for parsing this 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);
    }

However, I am getting the following exception while parsing the above mentioned CSV-format file:

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]

This is because the second line is empty. I need to skip the first two lines, how do I tell jackson to skip the first two lines of the file?

Edit 1: Here is the CsvModel file:

@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;
    }
}

Upvotes: 1

Views: 9579

Answers (4)

Ghost Rider
Ghost Rider

Reputation: 161

If anyone is still looking for a solution:

    File tempFile = new File(tempLocation + csvFile.getName()); // tempLocation as per your choice
    List<String> lines = FileUtils.readLines(csvFile, encoding); // apache.commons - FileUtils
    if(!importingFile.exists())
        importingFile.createNewFile();

    int lineNum = 0;
    PrintWriter printWriter = new PrintWriter(importingFile, "UTF-8");
    for(String line : lines){

        if(lineNum == 0){ continue; } // Skip the first line, if required

        /**
         * Here removes all special characters other than numbers and alphabets.
         * Because in my case there was some unknown characters in the file.
         */
        String filtered = line.replaceAll("[^a-zA-Z0-9]", "");
        if( null == filtered || "".equals(filtered.trim()))){
            continue;
        }
        printWriter.println(line);
        lineNum++;
    }
    if(printWriter != null) printWriter.close();

    CsvMapper mapper = new CsvMapper(); //  jackson.dataformat - CsvMapper
    mapper.enable(CsvParser.Feature.SKIP_EMPTY_LINES);

    CsvSchema schema = mapper.schemaFor(CsvModel.class).withHeader()
            .withColumnSeparator(';')
            .withColumnReordering(true);
    ObjectReader reader = mapper.readerFor(CsvModel.class).with(schema);

    List list = reader.readValues(new FileInputStream(tempFile)).readAll();

Upvotes: 0

Sasha Yakobchuk
Sasha Yakobchuk

Reputation: 611

To skip empty lines you can use SKIP_EMPTY_LINES feature:

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);

Upvotes: 5

blackr1234
blackr1234

Reputation: 1476

A better answer will be to consume the first few lines using BufferedReader.readLine().

Refer to this Apache commons csv skip lines.

Sample code:

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.");
}

Upvotes: 1

Darshan Mehta
Darshan Mehta

Reputation: 30819

You can skip the first two rows by advancing the iterator twice before processing, e.g.:

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

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

This will make sure no Exception gets thrown if let's say the file has less than 2 records.

Update

Editing the answer based on question edits:

The reason why it throws RuntimeJsonMappingException is because it is interpreting one line of vsc file as a string and is trying to bind String to CsvModel object. You need to specify a (non empty) schema with csvMapper mapper which acts as metadata while deserializing one line, e.g.:

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

You can have a look at this example.

Upvotes: 1

Related Questions