reikje
reikje

Reputation: 3064

Can you use Jacksons polymorphism with headerless CSV files of variable length?

I have a csv file that contains no header column(s). Each row contains a row type in it's first column and the row type will determine which columns the row will have. Here is a simplified version:

"TypeARow","20240210"
"TypeBRow","5610429110","test"

I am trying to parse this using jackson-dataformat-csv and polymorphism. My test code looks like this:

import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvParser;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import org.junit.jupiter.api.Test;

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

public class JacksonTest {
    @Test
    void testParsing() throws IOException {

        var mapper = new CsvMapper()
            .enable(CsvParser.Feature.WRAP_AS_ARRAY);

        var file = new File("sample.csv");
        var schema = CsvSchema.emptySchema().withoutHeader();

        try (var it = mapper.readerFor(CustomRow.class).with(schema).readValues(file)) {
            while (it.hasNext()) {
                Object row = it.next();
                System.out.println(row);
            }
        }

    }
}

I have modeled the row types like this:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = TypeARow.class, name = "TypeARow"),
    @JsonSubTypes.Type(value = TypeBRow.class, name = "TypeBRow"),
    // Add more subtypes if needed
})
public interface CustomRow {
    String getType();
}

@JsonDeserialize(as = TypeARow.class)
class TypeARow implements CustomRow {
    private final String type;
    private final String column1;

    public TypeARow(String column1) {
        this.type = "TypeARow";
        this.column1 = column1;
    }

    @Override
    public String getType() {
        return type;
    }
}

@JsonDeserialize(as = TypeBRow.class)
class TypeBRow implements CustomRow {
    private final String type;
    private final String column1;
    private String column2;

    public TypeBRow(String column1, String column2) {
        this.type = "TypeBRow";
        this.column1 = column1;
        this.column2 = column2;
    }

    @Override
    public String getType() {
        return type;
    }

}

The error I am currently seeing is:

com.fasterxml.jackson.databind.RuntimeJsonMappingException: Cannot construct instance of `com.package.TypeBRow` (no Creators, like default constructor, exist): no String-argument constructor/factory method to deserialize from String value ('5610429110')
 at [Source: (FileInputStream); line: 2, column: 12]

indicating that it fail to create the TypeBRow by calling the constructor with 2 elements. Any ideas?

Upvotes: 0

Views: 84

Answers (0)

Related Questions