Menu

#165 Automaticaly sorting columns on HeaderColumnNameMappingStrategy

wont-fix
None
5
2024-03-31
2024-03-17
Six Douglas
No

Hi again,
I'm creating another ticket for a second request/issue ;).
In my case, I would like to use the CsvBindByName annotation to fix the CSV header name and sort the output columns.
In order to do that, I've:
- force the mapping strategy to HeaderColumnNameMappingStrategy to use the CsvBindByName as name source.
- added a column name comparator using the setColumnOrderOnWrite method.

The comparator used is based on the CsvBindByPosition, this way the ouput is fully managed by the annotations.

Here is a sample code for this :

StringWriter sw = new StringWriter();
final HeaderColumnNameMappingStrategy<MyBean> nameMappingStrategy = new HeaderColumnNameMappingStrategyBuilder<MyBean>().build();
nameMappingStrategy.setType(MyBean.class);
final List<String> fieldList = Arrays.stream(MyBean.class.getFields())
                .filter(field -> field.isAnnotationPresent(CsvBindByPosition.class) && field.isAnnotationPresent(CsvBindByName.class))
                .sorted(Comparator.comparingInt(o -> o.getAnnotation(CsvBindByPosition.class).position()))
                .map(field -> field.getAnnotation(CsvBindByName.class))
                .map(CsvBindByName::column)
                .toList();
nameMappingStrategy.setColumnOrderOnWrite(Comparator.comparingInt(fieldList::indexOf));
StatefulBeanToCsv<MyBean> writer = new StatefulBeanToCsvBuilder<MyBean>(sw)
                .withMappingStrategy(nameMappingStrategy)
                .build();

Is it the right way to do this? If there is already a solution for this directly built in the lib? If there are none yet, do you think it would be possible to add this functionality directly in lib?
If you want, I can help with your guidance.
Thanks in advance

Discussion

  • Scott Conway

    Scott Conway - 2024-03-24

    Hello Six

    I don't have as much experience with this one as I have never personally had a use case where order of the output mattered. But that said there already exists an non-annotation way of handling it.

    There is some crude documentation at https://opencsv.sourceforge.net/#changing_the_write_order

    The headerColumnNameMappingStrategy setColumnOrderOnWrite method takes a comparator of strings. You can use something like the supplied LiteralComparator and a sample of its usage in ComparatorTest.java. https://opencsv.sourceforge.net/xref-test/com/opencsv/bean/comparator/ComparatorTest.html#ComparatorTest

    Hope that helps.

    Scott :)

     
  • Six Douglas

    Six Douglas - 2024-03-24

    Yes that's a possibility and I'm using the setColumnOrderOnWrite method in my sample code above, passing it comparator. But my point is about giving the possibility to use the CsvBindByPosition annotation in conjonction with the HeaderColumnNameMappingStrategy to do the column sorting automatically.

    In my use case, I'm using CSV files as output to validate the behaviour of my program. Our QA team give us some CSV files, directly extracted from the Database. We are planning to use plain text comparison to validate our computation against the expected files. That is why we have to respect the column ordering.

     
  • Scott Conway

    Scott Conway - 2024-03-31

    Six I apologize for taking so long to get back to this. Work and family has been keeping me busy.

    Me personally I am going to pass on this for two reasons:

    The first is I actually like your solution!! I believe it was what Andrew was envisioning when he designed the setColumnOrderOnWrite and seeing what you did made me actually understand what Andrew did. Hopefully between now and our next release (nothing planned) I am going to update the documentation ( https://opencsv.sourceforge.net/#changing_the_write_order) to have examples based on your code and the non deprecated comparators.

    The second reason is a mix of laziness and being jaded. What you did works! Works perfectly! And you can really use any annotation that you want - create your own and have your own sorting order. So it gives everyone who actually wants to sort a way of doing it however they want. My fear is that by forcing a default it may be harder to override it and honestly I neither want to put in that effort to find out nor think it neccessary given your solution works well, just requires a bit more code.

    As for the jaded part.... Well the intention of the CsvBindByPosition annotation is to bind the column position on read. I know in my heart of hearts that if I set it up to do both there will be the inevitiable requests of "don't use CsvBindByPosition on write because I prefer the default sorting on write" and "please create another annotation on write because I want to read in one order and write in a different order". and both of those cases are actually handled by the code you created. Sorry for sounding like a fawning fanboy but to me you actually hit upon pretty much the best solution and any attempts to make it easier is going to end up creating more work than it will save.

    Scott :)

     
  • Andrew Rucker Jones

    • status: open --> wont-fix
    • assigned_to: Scott Conway
     
  • Andrew Rucker Jones

    I just had a chance to read this. I had only half-understood it while scanning over the ticket early in the morning when I got up two weeks ago.

    I agree with Scott; this is a pretty good solution, and I can't think of a better way to do this with the built-in functionality of opencsv if you are determined to keep ordering information solely in the annotations. Well done.

     

Log in to post a comment.