GEDOPLAN

OpenCSV

Alle
csv smile 1

XML und JSON sind fantastische, leichtgewichtige und technisch gut zu verarbeitende Schnittstellen-Formate. Es gibt da aber ein weiteres Format, welches den Beiden den Rang abläuft, zumindest wenn es um die inflationäre Verwendung geht: CSV. Wenn wir einmal nicht darum herum kommen CSV Dateien zu lesen oder zu schreiben, sollten wir zumindest versuchen dies auf eine Art und Weise zu tun die unsere Nerven schont. Ein solcher Weg: OpenCSV

csv smile 2

Die Grundidee hinter OpenCSV ist weder neu noch kompliziert. Ähnlich wie bei JPA, JSON oder XML versehen wir unser Model oder DTO-Klassen mit Annotationen die später für das Lesen / Schreiben der Daten sorgen soll. Später im Programm übergeben wir den entsprechenden Funktionen nur einen InputStream und eine so annotierte Klasse und für jede Zeile innerhalb unserer CSV Datei erhalten wir eine Objekte-Instanz ( bzw. beim Schhreiben: für jede Objektinstanz eine Zeile in der CSV Datei).

Für die einfachste Form dieser Annotationen existieren zwei Varianten:

    @CsvBindByPosition(position = 0)
    private Long id;

    @CsvBindByName(column = "customerid")
    private Long customerid;

Hier wird festgelegt ob das Mapping basierend auf der Position (Variante 1) oder basierend auf einer Kopfzeile innerhalb der CSV Datei (Variante 2) geschehen soll. Das Einlesen einer CSV Datei ist dann schnell erledigt:

demo.csv

customerid;firstname;lastname;registerdate;discount
1;Max;Muster;01.05.2010;5
2;Adam;Bean;20.06.2011;10
3;Lisa;Müller;08.08.2012;15
4;Lischen;Müller;27.09.2013;20

einlesen:

 List<Customer> beans = new CsvToBeanBuilder<Customer>(...inputstream...)
               .withSeparator(';')
               .withType(Customer.class)
               .build().parse();

Der Vorteil liegt auf der Hand. Dank der Annotationen haben wir eine sehr gut verständliche und wartbare Struktur welche die CSV Datei wiederspiegelt.

Oftmals reicht ein solches „einfaches“ Mapping natürlich nicht aus, da wir möglicherweise ganze Objektgraphen in eine flache CSV-Struktur bringen wollen. Zum einen wird beim Schreiben von CSV Dateien der entsprechende Getter aufgerufen wo ein einfaches Mapping statt finden kann. Flexibler und nicht nur für das Schreiben sondern auch für das Lesen von CSV Dateien sind Converter die registriert werden können:

public class Material {

    @CsvBindByPosition(position = 0)
    private Long id;

    @CsvBindByPosition(position = 1)
    private String description;

    @CsvBindAndSplitByPosition(position = 2, elementType = Price.class, splitOn = "\\|", converter = PriceConverter.class)
    private List<Price> prices;

    @CsvBindByPosition(position = 3)
    private Double averagePrice;

    public Double getAveragePrice() {
        return prices.stream().map(Price::getPrice)
                .mapToDouble(BigDecimal::doubleValue)
                .average()
                .orElse(0.);
    }
}

PriceConverter.java :

public class PriceConverter extends AbstractCsvConverter {

    private final DecimalFormat decimalFormat = new DecimalFormat("#,##");

    @Override
    public String convertToWrite(Object value) throws CsvDataTypeMismatchException {
        final Price price = (Price) value;
        final String priceValue = decimalFormat.format((price).getPrice());
        final String until = price.getValidTo().format(DateTimeFormatter.ISO_DATE);
        final String from = price.getValidTo().format(DateTimeFormatter.ISO_DATE);
        return String.format("%s*%s*%s|", from, until, priceValue);
    }

    @Override
    public Object convertToRead(String value) throws CsvDataTypeMismatchException, CsvConstraintViolationException {
        final String[] splits = value.trim().split("\\*");
        LocalDate from=LocalDate.parse(splits[0], DateTimeFormatter.ISO_DATE);
        LocalDate to=LocalDate.parse(splits[1], DateTimeFormatter.ISO_DATE);
        BigDecimal price = new BigDecimal(splits[2]);
        return new Price(from, to, price);
    }
}

Für die Vollständigkeit, das Schreiben:

        Writer writer = new FileWriter("out.csv");
        StatefulBeanToCsv beanToCsv = new StatefulBeanToCsvBuilder(writer).withSeparator(';').build();
        beanToCsv.write(materials);
        writer.close();

Es gibt noch einige mehr Annotationen ( Datumsformat, individuelles Mapping für Schreiben und Lesen…) mit dessen Hilfe wir in den Prozess eingreifen können. Die Bibliothek ist bezüglich seines Umfangs sehr übersichtlich, bietet aber genug Stellen sehr individuell auf das Schreiben und Lesen von CSV Dateien ein zu wirken. Allemal ein Blick Wert.

Github? Klar.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Schulungen mit der selben Kategorie:

Blogkategorie: Alle
Es wurden keine Ergebnisse gefunden.

weitere Artikel

Kontakt

Brauchen Sie eine individuelle IT-Schulung, eine fundierte Beratung oder eine individuelle Softwareentwicklung? Dann sind Sie hier genau richtig!

Tim Neumann

Geschäftsleitung

GEDOPLAN GmbH
Stieghorster Straße 60
33605 Bielefeld

GEDOPLAN GmbH
Kantstraße 164
10623 Berlin

    Kontakt

    Tim Neumann

    Geschäftsleitung

    GEDOPLAN GmbH
    Stieghorster Straße 60
    33605 Bielefeld

    GEDOPLAN GmbH
    Kantstraße 164
    10623 Berlin

    Brauchen Sie eine individuelle IT-Schulung, eine fundierte Beratung oder eine individuelle Softwareentwicklung? Dann sind Sie hier genau richtig!