jackson-dataformats-text icon indicating copy to clipboard operation
jackson-dataformats-text copied to clipboard

[jackson-dataformat-csv:2.8.11/2.12.2]CsvMapper unrecognised the @JsonProperty

Open zhaoxi2000 opened this issue 3 years ago • 10 comments

the result is image

AppsFlyerRawDataBean.java.txt appsflyer-raw-data.csv

        final CsvMapper mapper = new CsvMapper();
        final CsvSchema schema = mapper.schemaFor(AppsFlyerRawDataBean.class);
        final ObjectReader objectReader = mapper.readerFor(AppsFlyerRawDataBean.class).with(schema);
        final Collection<AppsFlyerRawData> result = new ArrayList<>(20);

        try (final Reader reader = new FileReader(csv)) {
            final MappingIterator<AppsFlyerRawDataBean> records = objectReader.readValues(reader);
            while (records.hasNextValue()) {
                final AppsFlyerRawDataBean one = records.nextValue();
                if (isBlank(one)) {
                    continue;
                }
                result.add(one);
            }
        }

zhaoxi2000 avatar Apr 29 '21 00:04 zhaoxi2000

First things, first: PLEASE DO NOT USE

@JsonIgnoreProperties(ignoreUnknown = true)

on tests. This is VERY common way to hide simple problems that you could fix if you saw them. Only use that in production once other problems are resolved (if at all).

Second: if input contains header line (which I think is the case here), your schema must indicate this:

 schema = schema.withUseHeader(true);

You probably do not need create schema from POJO class either, as long as names from header line match expected property names (either from getter name, or via annotation).

With these changes you may be able to get better results and see if there are other problems.

cowtowncoder avatar Apr 29 '21 23:04 cowtowncoder

@cowtowncoder Do not use @JsonIgnoreProperties(ignoreUnknown = true) , I think it is not correct way because csv has 90 columns and programmer just need extracts 30 columns. CSV comes from the collaboration company.

Second: schema = schema.withUseHeader(true); I had a try a few days ago with the same result.

POJO property names does not match the CSV header. You can open the two files: appsflyer-raw-data.csv

zhaoxi2000 avatar May 09 '21 08:05 zhaoxi2000

To me header line names do seem to match annotated names?

  • Campaign on header row matches @JosonProperty("Campaign") of private String campaignName
  • Campaign ID similarly matches campaignId property

At this point I would need either:

  • Specific exception message of what fails? Which property/column is mismatching OR
  • Full unit test to reproduce the issue

cowtowncoder avatar May 09 '21 18:05 cowtowncoder

No any exception on fails. Do you need my full test-case?

zhaoxi2000 avatar May 14 '21 13:05 zhaoxi2000

A test case that shows the problem with assertions would help: the original picture simply showed a POJO with values, some of which were nulls. That does not really tell me much about what the problem is: what @JsonProperty seems to be ignored? (which POJO property should have gotten value from CSV but didn't?).

cowtowncoder avatar May 14 '21 17:05 cowtowncoder

Hello @cowtowncoder ,

I recently came across that issue because I faced the same problem within one of my projects. I created a repo containing a (hopefully meaningful) test case.

https://github.com/sbszcz/jackson-csv-java-example

Mapping the content of the columns does not work. I have no idea why. The content seems to be mapped to arbitrary pojo properties.

Could you please have a look at this?

Thank you, Sebastian

sbszcz avatar Jun 14 '21 09:06 sbszcz

Looking at the example @sbszcz (thank you!) -- my first instinct is that I don't understand why there is no fail for non-annotated case since there is no match from header names to actual names.

cowtowncoder avatar Jul 09 '21 03:07 cowtowncoder

@sbszcz A few issues with the code, solving of which will make things work.

First: you should not usually (need to) combine 2 types of CsvSchemas (one generated from POJOs, using specific ordering; another read from header) -- in this case I think only latter makes sense. Sometimes there are benefits from combining this way; but if so, you almost certainly want to use this line too:

csvSchema = csvSchema.withColumnReordering(true);

which ensures that the information is combined: otherwise explicitly specified schema (from POJO, and ordering it defines) is used.

So, instead of starting with mapper.schemaFor(Type.class), start with CsvSchema.emptySchema(). Second, since there are entries in input that do not map to POJO (at least in the test), disable DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES. This makes test with annotations pass.

The problem without using annotations is that there is no mapping between names from CSV file header (in German) and property names in PlainTransaction, so that cannot pass quite as specified. It would be possible to programmatically build CsvSchema, and then force skipping of the first (data) row:

schema = schema.withSkipFirstDataRow(true);

Or, even specify the expected ordering for POJO Fields with @JsonPropertyOrder: otherwise ordering used is alphabetic (JDK does not guarantee ordering for fields within class so CSV module defauilts to alphabetic ordering).

cowtowncoder avatar Jul 09 '21 03:07 cowtowncoder

@zhaoxi2000 I think you have the same problem: your POJO definition does not specify ordering of properties with @JsonPropertyOrder, and the default ordering is alphabetic. That would then map first 5 names into 5 columns, which is not what you want based on header line.

Instead leave out the whole schemaFor(AppsFlyerRawDataBean.class) and instead use .withHeader() when constructing CsvSchema.

cowtowncoder avatar Jul 09 '21 03:07 cowtowncoder

At this point I don't see actual bug to fix, but I want to help ensure that usage can be changed to work for the use case, so keeping issue open.

cowtowncoder avatar Jul 09 '21 03:07 cowtowncoder