Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

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

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 into CSV file as report.

I am using following annotations:

public class MappingsBean {

    @CsvBindByName(column = "TradeID")
    @CsvBindByPosition(position = 0)
    private String tradeId;

    @CsvBindByName(column = "GWML GUID", required = true)
    @CsvBindByPosition(position = 1)
    private String gwmlGUID;

    @CsvBindByName(column = "MXML GUID", required = true)
    @CsvBindByPosition(position = 2)
    private String mxmlGUID;

    @CsvBindByName(column = "GWML File")
    @CsvBindByPosition(position = 3)
    private String gwmlFile;

    @CsvBindByName(column = "MxML File")
    @CsvBindByPosition(position = 4)
    private String mxmlFile;

    @CsvBindByName(column = "MxML Counterparty")
    @CsvBindByPosition(position = 5)
    private String mxmlCounterParty;

    @CsvBindByName(column = "GWML Counterparty")
    @CsvBindByPosition(position = 6)
    private String gwmlCounterParty;
}

And then I use StatefulBeanToCsv class to write into CSV file:

File reportFile = new File(reportOutputDir + "/" + REPORT_FILENAME);
Writer writer = new PrintWriter(reportFile);
StatefulBeanToCsv<MappingsBean> beanToCsv = new 
                              StatefulBeanToCsvBuilder(writer).build();
beanToCsv.write(makeFinalMappingBeanList());
writer.close();

The problem with this approach is that if I use @CsvBindByPosition(position = 0) to control position then I am not able to generate column names. If I use @CsvBindByName(column = "TradeID") then I am not able to set position of the columns.

Is there a way where I can use both annotations, so that I can create CSV files with column headers and also control column position?

Regards, Vikram Pathania

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've had similar problem. AFAIK there is no build-in functionality in OpenCSV that will allow to write bean to CSV with custom column names and ordering.

There are two main MappingStrategyies that are available in OpenCSV out of the box:

  • HeaderColumnNameMappingStrategy: that allows to map CVS file columns to bean fields based on custom name; when writing bean to CSV this allows to change column header name but we have no control on column order
  • ColumnPositionMappingStrategy: that allows to map CSV file columns to bean fields based on column ordering; when writing bean to CSV we can control column order but we get an empty header (implementation returns new String[0] as a header)

The only way I found to achieve both custom column names and ordering is to write your custom MappingStrategy.

First solution: fast and easy but hardcoded

Create custom MappingStrategy:

class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
    private static final String[] HEADER = new String[]{"TradeID", "GWML GUID", "MXML GUID", "GWML File", "MxML File", "MxML Counterparty", "GWML Counterparty"};

    @Override
    public String[] generateHeader() {
        return HEADER;
    }
}

And use it in StatefulBeanToCsvBuilder:

final CustomMappingStrategy<MappingsBean> mappingStrategy = new CustomMappingStrategy<>();
mappingStrategy.setType(MappingsBean.class);

final StatefulBeanToCsv<MappingsBean> beanToCsv = new StatefulBeanToCsvBuilder<MappingsBean>(writer)
    .withMappingStrategy(mappingStrategy)
    .build();
beanToCsv.write(makeFinalMappingBeanList());
writer.close()

In MappingsBean class we left CsvBindByPosition annotations - to control ordering (in this solution CsvBindByName annotations are not needed). Thanks to custom mapping strategy the header column names are included in resulting CSV file.

The downside of this solution is that when we change column ordering through CsvBindByPosition annotation we have to manually change also HEADER constant in our custom mapping strategy.

Second solution: more flexible

The first solution works, but it was not good for me. Based on build-in implementations of MappingStrategy I came up with yet another implementation:

class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
    @Override
    public String[] generateHeader() {
        final int numColumns = findMaxFieldIndex();
        if (!isAnnotationDriven() || numColumns == -1) {
            return super.generateHeader();
        }

        header = new String[numColumns + 1];

        BeanField beanField;
        for (int i = 0; i <= numColumns; i++) {
            beanField = findField(i);
            String columnHeaderName = extractHeaderName(beanField);
            header[i] = columnHeaderName;
        }
        return header;
    }

    private String extractHeaderName(final BeanField beanField) {
        if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
            return StringUtils.EMPTY;
        }

        final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
        return bindByNameAnnotation.column();
    }
}

You can use this custom strategy in StatefulBeanToCsvBuilder exactly this same as in the first solution (remember to invoke mappingStrategy.setType(MappingsBean.class);, otherwise this solution will not work).

Currently our MappingsBean has to contain both CsvBindByName and CsvBindByPosition annotations. The first to give header column name and the second to create ordering of columns in the output CSV header. Now if we change (using annotations) either column name or ordering in MappingsBean class - that change will be reflected in output CSV file.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...