【问题标题】:StatefulBeanToCsv with Column headers带有列标题的 StatefulBeanToCsv
【发布时间】:2018-02-14 19:27:18
【问题描述】:

我正在使用opencsv-4.0 编写一个 csv 文件,我需要在输出文件中添加列标题。

这是我的代码。

public static void buildProductCsv(final List<Product> product,
        final String filePath) {

    try {

        Writer writer = new FileWriter(filePath);

        // mapping of columns with their positions
        ColumnPositionMappingStrategy<Product> mappingStrategy = new ColumnPositionMappingStrategy<Product>();
        // Set mappingStrategy type to Product Type
        mappingStrategy.setType(Product.class);
        // Fields in Product Bean
        String[] columns = new String[] { "productCode", "MFD", "EXD" };
        // Setting the colums for mappingStrategy
        mappingStrategy.setColumnMapping(columns);

        StatefulBeanToCsvBuilder<Product> builder = new StatefulBeanToCsvBuilder<Product>(writer);

        StatefulBeanToCsv<Product> beanWriter = builder.withMappingStrategy(mappingStrategy).build();
        // Writing data to csv file
        beanWriter.write(product);
        writer.close();

        log.info("Your csv file has been generated!");

    } catch (Exception ex) {
        log.warning("Exception: " + ex.getMessage());
    }

}

以上代码创建一个包含数据的 csv 文件。但它不包括该文件中的列标题。

如何添加列标题以输出 csv?

【问题讨论】:

    标签: java csv opencsv


    【解决方案1】:

    ColumnPositionMappingStrategy#generateHeader 返回空数组

    /**
     * This method returns an empty array.
     * The column position mapping strategy assumes that there is no header, and
     * thus it also does not write one, accordingly.
     * @return An empty array
     */
    @Override
    public String[] generateHeader() {
        return new String[0];
    }
    

    如果您从 BeanToCsv 构建器中删除 MappingStrategy

    // replace 
    StatefulBeanToCsv<Product> beanWriter = builder.withMappingStrategy(mappingStrategy).build();
    // with
    StatefulBeanToCsv<Product> beanWriter = builder.build(); 
    

    它将 Product 的类成员写为 CSV 标题

    如果您的 Product 类成员名称是

    "productCode", "MFD", "EXD"
    

    这应该是正确的解决方案

    否则,添加@CsvBindByName 注解

    import com.opencsv.bean.CsvBindByName;
    import com.opencsv.bean.StatefulBeanToCsv;
    import com.opencsv.bean.StatefulBeanToCsvBuilder;
    
    import java.io.FileWriter;
    import java.io.Writer;
    import java.util.ArrayList;
    import java.util.List;
    
    public class CsvTest {
    
        public static void main(String[] args) throws Exception {
            Writer writer = new FileWriter(fileName);
    
            StatefulBeanToCsvBuilder<Product> builder = new StatefulBeanToCsvBuilder<>(writer);
            StatefulBeanToCsv<Product> beanWriter = builder.build();
    
            List<Product> products = new ArrayList<>();
            products.add(new Product("1", "11", "111"));
            products.add(new Product("2", "22", "222"));
            products.add(new Product("3", "33", "333"));
            beanWriter.write(products);
            writer.close();
        }
    
        public static class Product {
            @CsvBindByName(column = "productCode")
            String id;
            @CsvBindByName(column = "MFD")
            String member2;
            @CsvBindByName(column = "EXD")
            String member3;
    
            Product(String id, String member2, String member3) {
                this.id = id;
                this.member2 = member2;
                this.member3 = member3;
            }
    
            public String getId() {
                return id;
            }
    
            public void setId(String id) {
                this.id = id;
            }
    
            public String getMember2() {
                return member2;
            }
    
            public void setMember2(String member2) {
                this.member2 = member2;
            }
    
            public String getMember3() {
                return member3;
            }
    
            public void setMember3(String member3) {
                this.member3 = member3;
            }
        }
    
    }
    

    输出:

    "EXD","MFD","PRODUCTCODE"

    "111","11","1"

    "222","22","2"

    "333","33","3"

    注意;由于 OpenCSV 库使用反射,类、getter 和 setter 需要公开

    【讨论】:

    • @Bishan 您可以为每个成员添加注释@CsvBindByPosition(position = 0),位置从零开始。
    • 我不能同时使用@CsvBindByPosition@CsvBindByName 吗?我添加了@CsvBindByPosition。现在列顺序没问题。但是输出文件中缺少列名。
    • 看来你是对的,我一直在到处寻找源代码,考虑在sourceforge.net/p/opencsv/feature-requests开票
    • 没问题,如果存在注解CsvBindByPosition或CsvCustomBindByPosition,则选择ColumnPositionMappingStrategy,不生成headers,ColumnPositionMappingStrategy#generateHeader返回空数组
    • 有没有办法在 CsvBindByName 注释中得到确切的名称?而不是它的大写?
    【解决方案2】:

    你可以通过注解来追加

    public void export(List<YourObject> list, PrintWriter writer) throws Exception {
            writer.append( buildHeader( YourObject.class ) );
            StatefulBeanToCsvBuilder<YourObject> builder = new StatefulBeanToCsvBuilder<>( writer );
            StatefulBeanToCsv<YourObject> beanWriter = builder.build();
            beanWriter.write( mapper.map( list ) );
            writer.close();
        }
    
        private String buildHeader(Class<YourObject> clazz) {
            return Arrays.stream( clazz.getDeclaredFields() )
                    .filter( f -> f.getAnnotation( CsvBindByPosition.class ) != null
                            && f.getAnnotation( CsvBindByName.class ) != null )
                    .sorted( Comparator.comparing( f -> f.getAnnotation( CsvBindByPosition.class ).position() ) )
                    .map( f -> f.getAnnotation( CsvBindByName.class ).column() )
                    .collect( Collectors.joining( "," ) ) + "\n";
        }
    
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public class YourObject {
    
        @CsvBindByPosition(position = 0)
        @CsvBindByName(column = "A")
        private Long a;
    
        @CsvBindByPosition(position = 1)
        @CsvBindByName(column = "B")
        private String b;
    
        @CsvBindByPosition(position = 2)
        @CsvBindByName(column = "C")
        private String c;
    
    }
    

    【讨论】:

    • 流的优雅使用。这似乎是最好的实现。
    【解决方案3】:

    我可能在这里遗漏了一些明显的东西,但你不能将你的标题字符串附加到 writer 对象吗?

    Writer writer = new FileWriter(filePath);
    writer.append("header1, header2, header3, ...etc \n");
    
    // This will be followed by your code with BeanToCsvBuilder 
    // Note: the terminating \n might differ pending env.
    

    【讨论】:

      【解决方案4】:

      使用 HeaderColumnNameMappingStrategy 进行读取,然后使用相同的策略进行写入。在这种情况下,“相同”不仅意味着同一个类,而且实际上是同一个对象。

      来自StatefulBeanToCsvBuilder.withMappingStrategy的javadoc:

      读取 CSV 源代码,从读取操作中获取映射策略,并将其传递给此方法进行写入操作是完全合法的。这节省了一些处理时间,但更重要的是,保留了标头顺序。

      这样您将获得一个包含标题的 CSV,其中列的顺序与原始 CSV 相同。

      使用 OpenCSV 5.4 为我工作。

      【讨论】:

        【解决方案5】:

        您也可以重写 generateHeaders 方法并返回设置的列映射,它将在 csv 中具有标题行

        ColumnPositionMappingStrategy<Product> mappingStrategy = new ColumnPositionMappingStrategy<Product>() {
                    @Override
                    public String[] generateHeader(Product bean) throws CsvRequiredFieldEmptyException {
                        return this.getColumnMapping();
                    }
                };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-09-05
          • 2012-01-01
          • 2016-02-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多