I'm trying to use CsvMapper to parse a Java 16 Record out of a CSV file. While testing it I've encountered some strange behaviour:
It seems CsvMapper.schemaFor(AnyRecord.class).withHeader() always returns a schema where the columns are assumed to be sorted alphabetically by name and the header row is not considered.
Here's an example:
public record TestRecord(String c, String b, String a) {
}
Running the JUnit 5 test:
@Test
void testRecord() throws JsonProcessingException {
CsvMapper csvMapper = new CsvMapper();
var schema = csvMapper.schemaFor(TestRecord.class).withHeader();
var csv = """
c,b,a
C,B,A""";
TestRecord tr = csvMapper.readerFor(TestRecord.class).with(schema).readValue(csv);
Assertions.assertEquals(new TestRecord("C", "B", "A"), tr);
}
Will fail with:
expected: <TestRecord[c=C, b=B, a=A]> but was: <TestRecord[c=A, b=B, a=C]>
Expected :TestRecord[c=C, b=B, a=A]
Actual :TestRecord[c=A, b=B, a=C]
So, the parsed instance of TestRecord was initialised in the wrong order.
Doing the same thing with a class:
import java.util.Objects;
public class TestClass {
public String a;
public String b;
public String c;
public TestClass(String a, String b, String c) {
this.a = a;
this.b = b;
this.c = c;
}
public TestClass() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestClass testClass = (TestClass) o;
return Objects.equals(a, testClass.a) && Objects.equals(b, testClass.b) && Objects.equals(c, testClass.c);
}
@Override
public int hashCode() {
return Objects.hash(a, b, c);
}
}
Running the test:
@Test
void testClass() throws JsonProcessingException {
CsvMapper csvMapper = new CsvMapper();
var schema = csvMapper.schemaFor(TestClass.class).withHeader();
var csv = """
c,b,a
C,B,A""";
TestClass tr = csvMapper.readerFor(TestClass.class).with(schema).readValue(csv);
Assertions.assertEquals(new TestClass("C", "B", "A"), tr);
}
Which passes fine!
Having a record where the properties are ordered alphabetically by name:
public record TestRecordWithSortedProperties(String a, String b, String c) {
}
A similar test case to the first works fine (not the CSV data is still ordered c,b,a):
@Test
void testRecordWithSortedProperties() throws JsonProcessingException {
CsvMapper csvMapper = new CsvMapper();
var schema = csvMapper.schemaFor(TestRecordWithSortedProperties.class).withHeader();
var csv = """
c,b,a
C,B,A""";
TestRecordWithSortedProperties tr = csvMapper.readerFor(TestRecordWithSortedProperties.class).with(schema).readValue(csv);
Assertions.assertEquals(new TestRecordWithSortedProperties("C", "B", "A"), tr);
}
Also defining a schema directly will work:
@Test
void testRecordWithManualSchema() throws JsonProcessingException {
CsvMapper csvMapper = new CsvMapper();
var schema = CsvSchema.builder()
.addColumn("c")
.addColumn("b")
.addColumn("a")
.build()
.withHeader();
var csv = """
c,b,a
C,B,A""";
TestRecord tr = csvMapper.readerFor(TestRecord.class).with(schema).readValue(csv);
Assertions.assertEquals(new TestRecord("C", "B", "A"), tr);
}
While an annotated record won't again:
public record AnnotatedTestRecord(@JsonProperty("c") String c, @JsonProperty("b") String b, @JsonProperty("a") String a) {
}
Using @JsonPropertyOrder works well!
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({"c", "b", "a"})
public record TestRecord(String c, String b, String a) {
}
I guess the actual errors lies somewhere in the CSV deserilaizer, since alphabetical property order seems to be the default in jackson. I tried to debug into the whole process but couldn't pin point it.
I'm trying to use
CsvMapperto parse a Java 16 Record out of a CSV file. While testing it I've encountered some strange behaviour:It seems
CsvMapper.schemaFor(AnyRecord.class).withHeader()always returns a schema where the columns are assumed to be sorted alphabetically by name and the header row is not considered.Here's an example:
Running the JUnit 5 test:
Will fail with:
So, the parsed instance of
TestRecordwas initialised in the wrong order.Doing the same thing with a class:
Running the test:
Which passes fine!
Having a record where the properties are ordered alphabetically by name:
A similar test case to the first works fine (not the CSV data is still ordered c,b,a):
Also defining a schema directly will work:
While an annotated record won't again:
Using
@JsonPropertyOrderworks well!I guess the actual errors lies somewhere in the CSV deserilaizer, since alphabetical property order seems to be the default in jackson. I tried to debug into the whole process but couldn't pin point it.