13

we have a table with 350+ columns. pojo class is generated and getters order get messed up. trying to use csvmapper from jackson, but it generates csv based on getter order. @JsonPropertyOrder is also not use feasible because of many columns.we maintain column ordering in xml and can generate field order array at runtime. can we override at runtime to provide array of fieldnames for property ordering? can we customize using annotation introspector?

user2598799
  • 141
  • 1
  • 1
  • 7

4 Answers4

18

What you are looking for is called a MappingFeature. You need to disable alphanumeric sorting of properties, which is enabled by default:

CsvMapper mapper = new CsvMapper();
mapper.disable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

More on this you can find here: Add a feature in CsvSchema to allow definition of ordering #42

  • It works, but (very) occasionally my unit test that checks the layout of the csv fails because the fields are not in the desired order. – Steven Kuypers Mar 10 '21 at 12:23
6

Just in case you get here in 2020, this comes straight from the documentation.

So how do you get a CSV Schema instance to use? There are 3 ways:

  • Create schema based on a Java class
  • Build schema manually.
  • Use the first line of CSV document to get the names (no types) for Schema Here is code for above cases:
// Schema from POJO (usually has @JsonPropertyOrder annotation)
CsvSchema schema = mapper.schemaFor(Pojo.class);

// Manually-built schema: one with type, others default to "STRING"
CsvSchema schema = CsvSchema.builder()
        .addColumn("firstName")
        .addColumn("lastName")
        .addColumn("age", CsvSchema.ColumnType.NUMBER)
        .build();

// Read schema from the first line; start with bootstrap instance
// to enable reading of schema from the first line
// NOTE: reads schema and uses it for binding
CsvSchema bootstrapSchema = CsvSchema.emptySchema().withHeader();
ObjectMapper mapper = new CsvMapper();
mapper.readerFor(Pojo.class).with(bootstrapSchema).readValue(json);
Peter Catalin
  • 1,342
  • 1
  • 14
  • 22
1

Note that @JsonPropertyOrder does not necessarily have to include all properties, just ones you are to include for serialization. But to indicate what is to be serialized you may need to use combination of @JsonProperty (to indicate properties to serialize) and different visibility for inclusion (either via ObjectMapper.setVisibility() for defaults, or via @JsonAutoDetect for per-POJO).

But assuming you do not want to use @JsonPropertyOrder, you can:

  1. Override method in JacksonAnnotationIntrospector that reads the annotation, provide your own implementation that uses other sources (does not need to come from annotations at all)
  2. If using Jackson 2.8.0, there is new way to specify per-class defaults for some things (see ObjectMapper.configOverride() object), including property order

Similarly you could override method that looks for @JsonProperty (findNameForDeserialization() and/or findNameForSerialization()) if you want to use custom criteria for inclusion/exclusion. There are other mechanisms for inclusion/exclusion as well, like JSON Views (@JsonView), JSON Filters.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
0

I believe your only choice here is uniVocity-parsers, as it allows you to choose which columns to write and in what order:

CsvWriterSettings settings = new CsvWriterSettings();
// Sets the file headers (used for selection only, these values won't be written automatically)
settings.setHeaders("Year", "Make", "Model", "Description", "Price");

// Selects which fields from the input should be written. In this case, fields "make" and "model" will be empty
// The field selection is not case sensitive
settings.selectFields("description", "price", "year");

//configures the writer process java beans with annotations (assume TestBean has a few annotated fiedls)
settings.setRowWriterProcessor(new BeanWriterProcessor<TestBean>(TestBean.class));

// Creates a writer with the above settings;
CsvWriter writer = new CsvWriter(new File("/path/to/output.csv"), settings);

// Writes the headers specified in the settings
writer.writeHeaders();

//creates a bean instance for writing
TestBean bean = new TestBean();
bean.setPrice(new BigDecimal("500.33"));
bean.setDescription("Blah,blah");
bean.setYear(1997);

//writes it
writer.processRecord(bean);

writer.close();

Hope it helps.

Disclosure: I'm the author of this libary, it's open-source and free (Apache 2.0 License)

Jeronimo Backes
  • 6,141
  • 2
  • 25
  • 29