Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically assign headers to a csv file using CsvMapper in Java

Can anyone help please? I am stuck on reading a csv file and serializing it onto a POJO. I am using CsvMapper from jackson library. The reading and serialization part are done and works fine-ish. The issue is when the user moves the headers/columns around causing the serialization to make some alphabetical assumption that the values on the CSV file are also alphabetically.
Eg (File below has headers on the first row and the second row has person details values) personNameHeader,personAgeHeader Wiliam,32

Now my POJO is as follow

@JsonIgnoreProperties(ignoreUnknown = true)
// @JsonPropertyOrder(value = {"personNameHeader", "personAgeHeader" })
public class PersonDetailsCSVTemplate  {

@JsonProperty("personNameHeader")
private String name;

@JsonProperty("personAgeHeader")
private String age;

//Public constructor and getters and setters...

This is the code to read the values from the CSV and map onto the class

import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
...

    CsvMapper csvMapper = new CsvMapper();    
    CsvSchema schema = csvMapper.typedSchemaFor(PersonDetailsCSVTemplate.class).withHeader();
          MappingIterator<PersonDetailsCSVTemplate  > dataIterator = csvMapper.readerFor(PersonDetailsCSVTemplate.class).with(schema)
            .readValues(data);

     while (dataIterator.hasNextValue()) {
        PersonDetailsCSVTemplate dataCSV = dataIterator.nextValue();
}

After the serialization it can be seen that CsvMapper mapped the following: PersonDetailsCSVTemplate.name = "32" andPersonDetailsCSVTemplate.age = "Wiliam"

By annotating the class with @JsonPropertyOrder(value = {"personNameHeader", "personAgeHeader" }) forces the CSV to be always name column followed by age column which isnt ideal.

Can anyone suggest anything that they think will work? Regards

like image 565
Wil Ferraciolli Avatar asked Jun 27 '17 16:06

Wil Ferraciolli


2 Answers

Since Jackson 2.7, you can use withColumnReordering(true) instead of sortedBy()

CsvSchema schema = csvMapper
    .typedSchemaFor(PersonDetailsCSVTemplate.class)
    .withHeader()
    .withColumnReordering(true);
like image 95
Nathan Avatar answered Sep 21 '22 08:09

Nathan


You could use sortedBy and give it the order in which the properties apeare in your csv data:

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder(value = { "personNameHeader", "personAgeHeader" })
static class PersonDetailsCSVTemplate
{
    @JsonProperty("personNameHeader")
    private String name;

    @JsonProperty("personAgeHeader")
    private String age;

    public String getName()
    {
        return name;
    }

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

    public String getAge()
    {
        return age;
    }

    public void setAge(String age)
    {
        this.age = age;
    }

    @Override
    public String toString()
    {
        return "PersonDetailsCSVTemplate [name=" + name + ", age=" + age + "]";
    }
}

You can keep or leave @JsonPropertyOrder it won't affect the output.

@Test
public void sort() throws IOException
{
    CsvMapper csvMapper = new CsvMapper();
    CsvSchema schema = csvMapper
        .typedSchemaFor(PersonDetailsCSVTemplate.class)
        .withHeader()
        .sortedBy("personNameHeader", "personAgeHeader")
        .withColumnSeparator(',')
        .withComments();

    MappingIterator<PersonDetailsCSVTemplate> dataIterator =
        csvMapper
            .readerFor(PersonDetailsCSVTemplate.class)
            .with(schema)
            .readValues("personNameHeader,personAgeHeader\r\n"
                +
                "Wiliam,32\r\n");

    while (dataIterator.hasNextValue())
    {
        PersonDetailsCSVTemplate dataCSV = dataIterator.nextValue();

        System.out.println(dataCSV);
    }
}

Output:

PersonDetailsCSVTemplate [name=Wiliam, age=32]
like image 35
A4L Avatar answered Sep 25 '22 08:09

A4L