2012-11-22 33 views
22

我有一個CSV文件,其中包含以下列:id,fnametelephonelname,addressOpenCSV - 如何將選定的列映射到Java Bean,而不管順序如何?

我有一個Person類與id,fnamelname數據成員。我想僅將這些列映射到來自CSV文件的Person對象,並丟棄telephoneaddress列。我怎樣才能做到這一點?隨着未來添加更多列,解決方案必須擴展。不管列位置,都應該工作。

在理想的解決方案中,用戶只能指定要讀取的列,它應該可以正常工作。

回答

4

我不能opencsv說話,但是這是很容易實現的使用Super CSV,它有兩個不同的readers支持partial reading(忽略列)以及讀入Javabean。 CsvDozerBeanReader甚至能夠deep and index-based mapping,所以你可以映射到嵌套字段。

我們(超級CSV團隊)剛剛發佈了2.0.0版本,可以從Maven central或SourceForge上獲得。

更新

下面是一個例子(根據你所創建的GitHub的項目測試),使用超CSV代替opencsv的。請注意,CSV偏好設置需要啓用surroundingSpacesNeedQuotes標誌,因爲您的示例CSV文件無效(其字段之間有空格 - 空格被視爲CSV中的數據的一部分)。

ICsvBeanReader beanReader = null; 
try { 
    beanReader = new CsvBeanReader(
      new InputStreamReader(
        ClassLoader.getSystemResourceAsStream("test.csv")), 
      new CsvPreference.Builder(CsvPreference.STANDARD_PREFERENCE) 
        .surroundingSpacesNeedQuotes(true).build()); 

    List<String> columnsToMap = Arrays.asList("fname", "telephone", "id"); 

    // read the CSV header (and set any unwanted columns to null) 
    String[] header = beanReader.getHeader(true); 
    for (int i = 0; i < header.length; i++) { 
     if (!columnsToMap.contains(header[i])) { 
      header[i] = null; 
     } 
    } 

    Person person; 
    while ((person = beanReader.read(Person.class, header)) != null) { 
     System.out.println(person); 
    } 

} finally { 
    beanReader.close(); 
} 
25

您可以使用HeaderColumnNameTranslateMappingStrategy。讓我們假設您的CSV包含以下列:Id,Fname,Telephone,Lname,Address爲簡單起見。

CsvToBean<Person> csvToBean = new CsvToBean<Person>(); 

Map<String, String> columnMapping = new HashMap<String, String>(); 
columnMapping.put("Id", "id"); 
columnMapping.put("Fname", "fname"); 
columnMapping.put("Lname", "lname"); 

HeaderColumnNameTranslateMappingStrategy<Person> strategy = new HeaderColumnNameTranslateMappingStrategy<Person>(); 
strategy.setType(Person.class); 
strategy.setColumnMapping(columnMapping); 

List<Person> list = null; 
CSVReader reader = new CSVReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("test.csv"))); 
list = csvToBean.parse(strategy, reader); 

columnMapping將與您的Person對象映射列。

+1

難道是可能將csv'「2015-01-02 23:59:50」'中的字符串映射到bean中的joda DateTime對象? – root

+0

此方法已過時,所以我寫了新的答案,它顯示瞭如何使用BeanBuilder https://stackoverflow.com/a/48227474/1238944 – agilob

0

https://github.com/arnaudroger/SimpleFlatMapper的最新版本0.9.4 現在有一個CsvMapper。 它使用標題匹配屬性名稱,或者如果沒有標題,您可以通過構建器指定列名稱。它支持構造函數,setter和字段注入。從InputStream或Reader讀取。

public class MyParser { 
    private final CsvMapper<MyObject> mapper = 
      CsvMapperFactory.newInstance().newMapper(MyObject.class); 
    public void writeAllObjectToLambda(Writer writer, InputStream is) throws IOException { 
     mapper.forEach(is, (o) -> writer.append(o.toString()).append("\n")); 
    } 
} 
3

使用uniVocity-parsers並且完成它。不管輸入CSV中的列是如何組織的,只有你需要的將被解析。

如果寫入,您在班級中的列將被寫入正確的列,而其他列將被清空。

下面是一些例子類:

class TestBean { 

    // if the value parsed in the quantity column is "?" or "-", it will be replaced by null. 
    @NullString(nulls = { "?", "-" }) 
    // if a value resolves to null, it will be converted to the String "0". 
    @Parsed(defaultNullRead = "0") 
    private Integer quantity; // The attribute type defines which conversion will be executed when processing the value. 

    @Trim 
    @LowerCase 
    // the value for the comments attribute is in the column at index 4 (0 is the first column, so this means fifth column in the file) 
    @Parsed(index = 4) 
    private String comments; 

    // you can also explicitly give the name of a column in the file. 
    @Parsed(field = "amount") 
    private BigDecimal amount; 

    @Trim 
    @LowerCase 
    // values "no", "n" and "null" will be converted to false; values "yes" and "y" will be converted to true 
    @BooleanString(falseStrings = { "no", "n", "null" }, trueStrings = { "yes", "y" }) 
    @Parsed 
    private Boolean pending; 
} 

這裏是如何得到TestBean

BeanListProcessor<TestBean> rowProcessor = new BeanListProcessor<TestBean>(TestBean.class); 

CsvParserSettings parserSettings = new CsvParserSettings(); 
parserSettings.setRowProcessor(rowProcessor); 
parserSettings.setHeaderExtractionEnabled(true); 

CsvParser parser = new CsvParser(parserSettings); 
parser.parse(getReader("/examples/bean_test.csv")); 

List<TestBean> beans = rowProcessor.getBeans(); 

披露的清單:我這個庫的作者。它是開放源代碼和免費的(Apache V2.0許可證)。

0

看看jcsvdao,https://github.com/eric-mckinley/jcsvdao/,使用hibernate樣式的映射文件,可以處理1to1和1toMany的關係。 好,如果你沒有擁有靈活的匹配策略的csv文件。

+0

歡迎來到SO。如果你在這裏包含一些代碼,而不是鏈接後面,這將是一個更好的答案。 – Teepeemm

0

jcvsdao示例用法

樣品用戶CSV文件

Username, Email, Registration Date, Age, Premium User 
Jimmy, [email protected], 04-05-2016, 15, Yes, M 
Bob, [email protected], 15-01-2012, 32, No, M 
Alice, [email protected], 22-09-2011, 24, No, F 
Mike, [email protected], 11-03-2012, 18, Yes, M 
Helen, [email protected], 02-12-2013, 22, Yes, F 
Tom, [email protected], 08-11-2015, 45, No, M 

創建CsvDao

CSVDaoFactory factory = new CSVDaoFactory("/csv-config.xml"); 
CSVDao dao = new CSVDao(factory); 
List<UserDetail> users = dao.find(UserDetail.class); 

CSV-config.xml中

<CSVConfig> 
    <mappingFiles fileType="resource"> 
     <mappingFile>/example01/mapping/UserDetail.csv.xml</mappingFile> 
    </mappingFiles> 
</CSVConfig> 

UserDetail.csv.xml

<CSVMapping className="org.jcsvdao.examples.example01.model.UserDetail" csvFile="csv-examples/example01/users.txt" delimiter="," ignoreFirstLine="true"> 
    <matchAll/> 
    <properties> 
     <property index="0" property="username" primaryKey="true"/> 
     <property index="1" property="email"/> 
     <property index="2" property="registrationDate" converter="myDateConverter"/> 
     <property index="3" property="age"/> 
     <property index="4" property="premiumUser" converter="yesNoConverter"/> 
     <property index="5" property="gender" converter="myGenderConverter"/> 
    </properties> 
    <converters> 
     <dateConverter converterName="myDateConverter" format="dd-MM-yyyy"/> 
     <booleanConverter converterName="yesNoConverter" positive="Yes" negative="No"/> 
     <customConverter converterName="myGenderConverter" converterClass="org.jcsvdao.examples.example01.converter.GenderCustomerConverter"/> 
    </converters> 
</CSVMapping> 
1

這裏是一個很好的辦法做到使用OpenCSV做映射到一般POJO:

protected <T> List<T> mapToCSV(String csvContent, Class<T> mapToClass) { 
    CsvToBean<T> csvToBean = new CsvToBean<T>(); 

    Map<String, String> columnMapping = new HashMap<>(); 
    Arrays.stream(mapToClass.getDeclaredFields()).forEach(field -> { 
     columnMapping.put(field.getName(), field.getName()); 
    }); 

    HeaderColumnNameTranslateMappingStrategy<T> strategy = new HeaderColumnNameTranslateMappingStrategy<T>(); 
    strategy.setType(mapToClass); 
    strategy.setColumnMapping(columnMapping); 

    CSVReader reader = new CSVReader(new StringReader(csvContent)); 
    return csvToBean.parse(strategy, reader); 
} 


public static class MyPojo { 
    private String foo, bar; 

    public void setFoo(String foo) { 
     this.foo = foo; 
    } 

    public void setBar(String bar) { 
     this.bar = bar; 
    } 
} 

然後從你的測試,你可以使用:

List<MyPojo> list = mapToCSV(csvContent, MyPojo.class); 
0

最近OpenCSV版本棄用方法parse(X, Y),它重新開始使用BeanBuilder,所以最好的答案是過時的。

try { 
    CsvToBeanBuilder<PersonCSV> beanBuilder = new CsvToBeanBuilder<>(new InputStreamReader(new FileInputStream("your.csv"))); 

    beanBuilder.withType(PersonCSV.class); 
    // build methods returns a list of Beans 
    beanBuilder.build().parse().forEach(e -> log.error(e.toString())); 

} catch (FileNotFoundException e) { 
    log.error(e.getMessage(), e); 
} 

這種方法可以讓你清理代碼並刪除MappingStrategy(如果你喜歡意大利麪條,你仍然可以使用它),這樣你就可以標註您的CSV類,如下所示:

@CsvDate("dd/MM/yyyy hh:mm:ss") 
@CsvBindByName(column = "Time Born", required = true) 
private Date birthDate; 
相關問題