OpenCSV: How to create CSV file from POJO with custom column headers and custom column positions?

后端 未结 19 1455
温柔的废话
温柔的废话 2020-12-08 04:14

I have created a MappingsBean class where all the columns of the CSV file are specified. Next I parse XML files and create a list of mappingbeans. Then I write that data int

相关标签:
19条回答
  • 2020-12-08 05:13

    If you're only interested in sorting the CSV columns based on the order in which member variables appear in your model class (CsvRow row in this example), then you can use a Comparator implementation to solve this in a rather simple manner. Here's an example that does this in Kotlin:

    class ByMemberOrderCsvComparator : Comparator<String> {
    
        private val memberOrder by lazy {
            FieldUtils.getAllFields(CsvRow::class.java)
                    .map { it.getDeclaredAnnotation(CsvBindByName::class.java) }
                    .map { it?.column ?: "" }
                    .map { it.toUpperCase(Locale.US) } // OpenCSV UpperCases all headers, so we do this to match
        }
    
        override fun compare(field1: String?, field2: String?): Int {
            return memberOrder.indexOf(field1) - memberOrder.indexOf(field2)
        }
    
    }
    

    This Comparator does the following:

    1. Fetches each member variable field in our data class (CsvRow)
    2. Finds all the ones with the @CsvBindByName annotation (in the order you specified them in the CsvRow model)
    3. Upper cases each to match the default OpenCsv implementation

    Next, apply this Comparator to your MappingStrategy, so it'll sort based off the specified order:

    val mappingStrategy = HeaderColumnNameMappingStrategy<OrderSummaryCsvRow>()
    mappingStrategy.setColumnOrderOnWrite(ByMemberOrderCsvComparator())
    mappingStrategy.type = CsvRow::class.java
    mappingStrategy.setErrorLocale(Locale.US)
    
    val csvWriter = StatefulBeanToCsvBuilder<OrderSummaryCsvRow>(writer)
                        .withMappingStrategy(mappingStrategy)
                        .build()
    

    For reference, here's an example CsvRow class (you'll want to replace this with your own model for your needs):

    data class CsvRow(
        @CsvBindByName(column = "Column 1")
        val column1: String,
    
        @CsvBindByName(column = "Column 2")
        val column2: String,
    
        @CsvBindByName(column = "Column 3")
        val column3: String,
    
        // Other columns here ...
    )
    

    Which would produce a CSV as follows:

    "COLUMN 1","COLUMN 2","COLUMN 3",...
    "value 1a","value 2a","value 3a",...
    "value 1b","value 2b","value 3b",...
    

    The benefit of this approach is that it removes the need to hard-code any of your column names, which should greatly simplify things if you ever need to add/remove columns.

    0 讨论(0)
提交回复
热议问题