2012-11-22 6 views
22

Ho un file CSV con le seguenti colonne: id, fname, telephone, lname, address.OpenCSV - Come mappare le colonne selezionate su Java Bean indipendentemente dall'ordine?

Ho una classe Person con id, fname e lname membri di dati. Voglio mappare solo queste colonne all'oggetto Person da un file CSV e scartare le colonne telephone e address. Come posso fare questo? La soluzione deve essere ridimensionata in quanto verranno aggiunte più colonne in futuro. E dovrebbe funzionare indipendentemente dalla posizione della colonna.

In una soluzione ideale, l'utente specificherà solo le colonne da leggere e dovrebbe funzionare.

risposta

0

Ho implementato una soluzione flessibile per affrontare questo problema. E 'molto semplice da usare e il codice con l'esempio è disponibile sul mio GitHub sotto:

https://github.com/jsinghfoss/opencsv

4

Non posso parlare per opencsv, ma questo è facilmente ottenibile usando Super CSV, che ha due differenti readers che supportano partial reading (ignorando le colonne), così come la lettura in un Javabean. CsvDozerBeanReader è persino in grado di deep and index-based mapping, quindi è possibile eseguire l'associazione ai campi nidificati.

Noi (il team Super CSV) abbiamo appena rilasciato la versione 2.0.0, disponibile da Maven central o SourceForge.

Aggiornamento

Ecco un esempio (in base al test nel progetto GitHub che hai creato), che utilizza Super CSV invece di opencsv. Nota le preferenze CSV necessario il flag surroundingSpacesNeedQuotes attivato come file CSV di esempio non è valido (ha spazi tra i campi - gli spazi sono considerati parte dei dati in 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

È possibile utilizzare HeaderColumnNameTranslateMappingStrategy. Supponiamo che il tuo CSV abbia le seguenti colonne: Id, Fname, Telephone, Lname, Address per motivi di semplicità.

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); 

Il ColumnMapping mapperà le colonne con il tuo Person oggetto.

+1

Sarebbe possibile mappare una stringa nel csv '" 2015-01-02 23:59:50 "' a un oggetto DateTime joda all'interno di un bean? – root

+0

Questo metodo è obsoleto, quindi ho scritto una nuova risposta che mostra come utilizzare BeanBuilder https://stackoverflow.com/a/48227474/1238944 – agilob

0

l'ultima versione di https://github.com/arnaudroger/SimpleFlatMapper 0.9.4 ha ora un CsvMapper. Usa l'intestazione per corrispondere al nome della proprietà o se nessuna intestazione è possibile specificare il nome della colonna attraverso il costruttore. Supporta il costruttore, il setter e l'iniezione sul campo. Leggi da InputStream o 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

Utilizzare uniVocity-parsers e utilizzarlo. Non importa in che modo le colonne sono organizzate nel CSV di input, verranno analizzate solo quelle di cui hai bisogno.

Se si scrive, le colonne che hai in classe verranno scritte nelle colonne corrette, mentre le altre saranno vuote.

Ecco una classe con alcuni esempi:

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; 
} 

Ecco come ottenere un elenco di 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(); 

Disclosure: Io sono l'autore di questa libreria. È open source e gratuito (licenza Apache V2.0).

0

Dai un'occhiata a jcsvdao, https://github.com/eric-mckinley/jcsvdao/, utilizza i file di mapping dello stile di ibernazione e può gestire le relazioni da 1 a 1 e da 1 a molti. Buono se non si possiedono i file CSV come hanno strategie di corrispondenza flessibili.

+0

Benvenuti in SO. Questa sarebbe una risposta molto migliore se includessi del codice qui, anziché dietro un link. – Teepeemm

0

esempio jcvsdao utilizzo

Esempio utente file 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 

creare un 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

Ecco un bel modo per fare uso OpenCSV a fare la mappatura POJO genericamente:

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; 
    } 
} 

Poi dal test è possibile utilizzare:

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

recenti OpenCSV deprecate il metodo parse(X, Y) e si ricomincia a utilizzare BeanBuilder, quindi la risposta più importante non è aggiornata.

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); 
} 

Questo metodo permette di ripulire il codice e rimuovere MappingStrategy (è comunque possibile utilizzare, se volete gli spaghetti), in modo da poter annotare la classe CSV come segue:

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