带列标题的StatefulBeanToCsv

vnjpjtjt  于 2023-09-27  发布在  其他
关注(0)|答案(7)|浏览(142)

我使用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?

5q4ezhmt

5q4ezhmt1#

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头写入
如果您的产品类成员名称是

"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库使用了Reflection,类、getters和setters需要是公共的

eivgtgni

eivgtgni2#

可以通过注解追加

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;

}
l7mqbcuq

l7mqbcuq3#

我可能错过了一些明显的东西,但你不能把你的头字符串附加到作家对象吗?

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.
gmol1639

gmol16394#

使用HeaderColumnNameMappingStrategy进行阅读,然后使用相同的策略进行写入。在这种情况下,“相同”意味着不仅仅是相同的类,而是实际上相同的对象。
StatefulBeanToCsvBuilder.withMappingStrategy的javadoc:
读取CSV源代码,从读取操作中获取Map策略,并将其传递给此方法进行写入操作,这是完全合法的。这节省了一些处理时间,但更重要的是,保留了头的顺序。
这样您将得到一个包含标题的CSV,其中列的顺序与原始CSV相同。
我使用OpenCSV 5.4。

ttygqcqt

ttygqcqt5#

使用自定义策略

static class CustomStrategy<T> extends ColumnPositionMappingStrategy<T> {
    public String[] generateHeader() {
        return this.getColumnMapping();
    }
}

在CSV对象上,不要忘记提供这两个

@CsvBindByName(column="UID")
@CsvBindByPosition(position = 3)
r3i60tvu

r3i60tvu6#

对于那些想同时使用HeaderName和Position而不先阅读csv文件的用户。
使用自定义Map策略很容易实现。

static class NameAndPositionMappingStrategy<T> extends HeaderNameBaseMappingStrategy<T> {
        public NameAndPositionMappingStrategy(Class<T> clazz) {
            setType(clazz);
            setColumnOrderOnWrite(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    CsvBindByPosition pos1 = getFieldMap().get(o1).getField().getAnnotation(CsvBindByPosition.class);
                    CsvBindByPosition pos2 = getFieldMap().get(o2).getField().getAnnotation(CsvBindByPosition.class);
                    return Integer.compare(
                       pos1 != null ? pos1.position() : 0,
                       pos2 != null ? pos2.position() : 0
                    );
                }
            });
        }
    }

在bean中,您现在可以定义两个注解

public static class Product {
        @CsvBindByPosition(position = 0)
        @CsvBindByName(column = "ID")
        String id;

        @CsvBindByPosition(position = 1)
        @CsvBindByName(column = "FIELD2")
        String member2;

        @CsvBindByPosition(position = 2)
        @CsvBindByName(column = "FIELD3")
        String member3;
    

}

作者应该看起来像这样:

builder.withMappingStrategy(new NameAndPositionMappingStrategy<Product>(Product.class).build()

这将同时使用CsvBindByName和CsvBindByPosition。
(或者,您也可以只使用HeaderNameBaseMappingStrategy并设置ColumnOrderOnWrite,而不使用继承)

gblwokeq

gblwokeq7#

您还可以重写generateHeaders方法并返回设置的列Map,它将在csv中具有header行

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

相关问题