【问题标题】:OpenCsv writes wrong column names with BeanToCsv + HeaderColumnNameTranslateMappingStrategyOpenCsv 使用 BeanToCsv + HeaderColumnNameTranslateMappingStrategy 写入错误的列名
【发布时间】:2015-11-18 10:57:43
【问题描述】:

我正在使用opencsv 3.6 来创建一个从 java bean 开始的 csv 文件。

首先,我尝试了这段代码:

import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.bean.BeanToCsv;
import com.opencsv.bean.HeaderColumnNameTranslateMappingStrategy;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class CustomBean {

    private String name;
    private String surname;

    public CustomBean(String n, String s) {
        this.name = n;
        this.surname = s;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getSurname() {
        return surname;
    }

    public static void main(String[] args) {
        Map<String,String> mapping = new HashMap<String,String>();
        mapping.put("COLUMN1","name");
        mapping.put("COLUMN2","surname");

        HeaderColumnNameTranslateMappingStrategy<CustomBean> strategy = new HeaderColumnNameTranslateMappingStrategy<CustomBean>();
        strategy.setType(CustomBean.class);
        strategy.setColumnMapping(mapping);

        ArrayList<CustomBean> customUsers = new ArrayList<CustomBean>();
        customUsers.add(new CustomBean("Kobe","Bryant"));

        BeanToCsv<CustomBean> bean = new BeanToCsv<CustomBean>();

        try {            
            CSVWriter writer = new CSVWriter(new FileWriter("testOut.csv"));
            bean.write(strategy, writer, customUsers);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

但我有以下错误:

Exception in thread "main" java.lang.RuntimeException: Error writing CSV !
    at com.opencsv.bean.BeanToCsv.write(BeanToCsv.java:74)
    at test.CustomBean.main(CustomBean.java:63)
Caused by: java.lang.NullPointerException
    at com.opencsv.bean.HeaderColumnNameTranslateMappingStrategy.getColumnName(HeaderColumnNameTranslateMappingStrategy.java:45)
    at com.opencsv.bean.HeaderColumnNameMappingStrategy.findDescriptor(HeaderColumnNameMappingStrategy.java:112)
    at com.opencsv.bean.BeanToCsv.processHeader(BeanToCsv.java:103)
    at com.opencsv.bean.BeanToCsv.write(BeanToCsv.java:69)
    ... 1 more

这是因为在HeaderColumnNameTranslateMappingStrategy 类的getColumnName 方法中的opencsv 源代码中有以下行:

return col < header.length ? columnMapping.get(header[col].toUpperCase()) : null;

因此,header 为空。确实如此,实际上这个类是HeaderColumnNameMappingStrategy 类的子类,其中包含从未初始化过的header 变量(String[] 类型)。

我在这个类中找到的唯一有用的方法是captureHeader,但不幸的是它需要一个CSVReader作为输入。

为此,我创建了一个空的 csv 文件:

COLUMN1,COLUMN2

我在 try/catch 块的开头添加了以下几行:

CSVReader reader = new CSVReader(new FileReader("testIn.csv"));
strategy.captureHeader(reader);

这样(我真的不喜欢,因为我必须创建一个csv文件)我也不例外,但是在结果csv文件中列的名称不遵循映射策略:

"name","surname"
"Kobe","Bryant"

两个问题:

  1. 如何获得预期的结果,即 csv 文件中的正确列名?
  2. 有办法不使用CSVReader 类吗?

【问题讨论】:

    标签: java csv opencsv


    【解决方案1】:

    查看BeanToCsv 的源代码,processHeader(...) 方法对提供的标头没有任何作用。您唯一的选择是创建自定义策略(以避免 CSVReader )和自定义 BeanToCsv 如下

    public class CustomBean { ... public static void main(String[] args) { ... HeaderColumnNameTranslateMappingStrategy strategy = new CustomStrategy<CustomBean>(); strategy.setType(CustomBean.class); strategy.setColumnMapping(mapping); ... BeanToCsv bean = new CustomBeanToCsv<CustomBean>(); ... } static class CustomStrategy<T> extends HeaderColumnNameTranslateMappingStrategy { @Override public void setColumnMapping(Map columnMapping) { super.setColumnMapping(columnMapping); header = new String[columnMapping.size()]; int i = 0; for (Map.Entry entry : columnMapping.entrySet()) { header[i] = entry.getKey().toUpperCase(); i++; } } public String[] getHeader() { return header; } } static class CustomBeanToCsv<T> extends BeanToCsv { @Override protected String[] processHeader(MappingStrategy mapper) throws IntrospectionException { if (mapper instanceof CustomStrategy) { return ((CustomStrategy) mapper).getHeader(); } else { return super.processHeader(mapper); } } } }

    【讨论】:

    • 感谢 Krishnaraj,您的解决方案是有效的。我只建议在 setColumnMapping 方法中移动标题数组的数量而不是构造函数,以避免双重映射并保持 opencsv api 的行为不变。我希望你能欣赏。
    • 是的,这很有道理。
    【解决方案2】:

    我已经使用 openCSV 五年了,我仍在学习有关它的东西。问题是 HeaderColumnNameMappingStrategy 和 HeaderColumnNameTranslateMappingStrategy 是为 CsvToBean 制作的。它希望读取一个文件以获取标题,以查看阅读器应该从哪里读取。

    对于 BeanToCsv 类,使用 ColumnPositionMappingStrategy 类。你给它你正在映射的类和你想要映射的列的列表,它会为你做剩下的事情。

    这是我编写的一个有效的小测试方法。

    public void createUsingBeanToCsv(int numRecords, FileWriter fos) {
        List<SmallRecord> smallRecords = new ArrayList<>(numRecords);
        for (int i = 0; i < numRecords; i++) {
            smallRecords.add(SmallRecordGenerator.createRecord(i));
        }
    
        BeanToCsv<SmallRecord> beanToCsv = new BeanToCsv<>();
        ColumnPositionMappingStrategy<SmallRecord> strategy = new ColumnPositionMappingStrategy<>();
        strategy.setType(SmallRecord.class);
        String[] columns = new String[]{"bigDecimal_1", "name_1", "intNumber_1"};
        strategy.setColumnMapping(columns);
    
        beanToCsv.write(strategy, fos, smallRecords);
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-18
      • 2021-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多