FlatFileItemReader

Alex_Wa

Mitglied
Hallo zusammen,

ich bin gerade dabei eine CSV-Datei auszulesen. Und die Werte die ich dort ausgelesen habe, sollen anschließend in eine Excel Datei importiert werden.

Jetzt versuche ich den FlatFileItemReader stand alone zu verwenden (kein Spring batch) um dann anschließend die Zeilen auf der Konsole ausgeben zu lassen.

Code sieht wie folgt aus:

Java:
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;

import org.springframework.core.io.FileSystemResource;

public class ReadOnly {
    
    public static void main(String[] args) throws UnexpectedInputException, ParseException, Exception {
        
        //ReadOnly read = new ReadOnly();
        String file = "H:\\TestCsv\\Test_1.csv";
        
        FlatFileItemReader<EmployeeModel> reader = new FlatFileItemReader<>();
        
        reader.setResource(new FileSystemResource(file));
        reader.setLinesToSkip(1);
        reader.setLineMapper(lineMapper());
        reader.open(new ExecutionContext());
        EmployeeModel line = reader.read();
        
        while(line != null) {
            line = reader.read();
            System.out.println(line);
        }
        
        reader.close();
        
    }
    
    
    private static LineMapper<EmployeeModel> lineMapper() {
        DefaultLineMapper<EmployeeModel> defaultLineMapper = new DefaultLineMapper<>();
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();

        lineTokenizer.setDelimiter(";");
        lineTokenizer.setNames(new String[] { "id", "vorname", "nachname" });

        BeanWrapperFieldSetMapper<EmployeeModel> fieldSetMapper = new BeanWrapperFieldSetMapper<>();

        defaultLineMapper.setLineTokenizer(lineTokenizer);
        defaultLineMapper.setFieldSetMapper(fieldSetMapper);

        return defaultLineMapper;
    }

}


Wenn ichd en Code ausführe tritt folgender Fehler auf:

Code:
Exception in thread "main" org.springframework.batch.item.file.FlatFileParseException: Parsing error at line: 2 in resource=[file [H:\TestCsv\Test_1.csv]], input=[1;alex;mueller]
    at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:189)
    at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:93)
    at lu.haas.burgissmerger.service.ReadOnly.main(ReadOnly.java:27)
Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Class.newInstance()" because "this.type" is null
    at org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper.getBean(BeanWrapperFieldSetMapper.java:248)
    at org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper.mapFieldSet(BeanWrapperFieldSetMapper.java:197)
    at org.springframework.batch.item.file.mapping.DefaultLineMapper.mapLine(DefaultLineMapper.java:43)
    at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:185)
    ... 2 more


Hat hierzu jemand eine Lösung ? Oder hat jemand schon Mal mit dem FlatFileItemReader eine csv ausgelesen?


Vielen Dank im Voraus.

Liebe Grüße!
 

Oneixee5

Top Contributor
Ich nehme an, der Fehler kommt da dein LineMapper keine @Bean ist. Für so eine einfache Aufgabe über das Spring-Framework zu gehen halte ich für etwas über motiviert. Wenn man unbedingt über eine externe Lib gehen möchte, dann bietet sich Apache Commons CSV an oder man schreibt die 5 Zeilen schnell selbst.
 

Alex_Wa

Mitglied
Auch wenn ich es mit @Bean definiere wird die Exception ausgegeben.

Das muss halt über Spring gehen weil das später ein Microservice für eine Webapplikation wird. Sorry hätte ich direkt erwähnen sollen. Mein Fehler!

Dass es bspw. über einen BufferedReader etc. schneller funktioniert wurde mir auch schon gesagt. Nur leider soll ich es mit dem FlatFileItemReader machen...
 

thecain

Top Contributor
Eine Webapp oder ein Batch? Ein FlatFileItemReader ist mehr als nur ein csv Parser sondern hat auch Spring Batch Logik. Deswegen braucht es zB. auch einen ExecutionContext.
Wenn du also nicht SoringBatch verwendest, würde ich vom FlatFileItemReader abraten (wenn es denn überhaupt möglich ist.)
 

Alex_Wa

Mitglied
Eine Webapp. Ich habe es bereits mit einem ExecutionContext probiert. Hat leider auch nicht funktioniert. Ich finde leider auch im Internet keine Antwort zu der Frage, ob es überhaupt ohne ein Batch möglich ist.
 

Oneixee5

Top Contributor
Was soll ich sagen, mit Beans funktioniert es:
Java:
package test;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.validation.BindException;

@SpringBootApplication
public class FileTestApplication implements CommandLineRunner {

    public static class EmployeeModel {

        private int id;
        private String vorname;
        private String nachname;

        public EmployeeModel() {
        }

        public int getId() {
            return this.id;
        }

        public void setId(final int id) {
            this.id = id;
        }

        public String getVorname() {
            return this.vorname;
        }

        public void setVorname(final String vorname) {
            this.vorname = vorname;
        }

        public String getNachname() {
            return this.nachname;
        }

        public void setNachname(final String nachname) {
            this.nachname = nachname;
        }

        @Override
        public String toString() {
            return "EmployeeModel [id=" + this.id + ", vorname=" + this.vorname + ", nachname=" + this.nachname + "]";
        }

    }

    public static class EmployeeFieldSetMapper implements FieldSetMapper<EmployeeModel> {

        @Override
        public EmployeeModel mapFieldSet(final FieldSet fieldSet) throws BindException {
            final EmployeeModel employee = new EmployeeModel();
            employee.setId(fieldSet.readInt("id"));
            employee.setVorname(fieldSet.readString("vorname"));
            employee.setNachname(fieldSet.readString("nachname"));
            return employee;
        }
    }

    private static Logger LOG = LoggerFactory
            .getLogger(FileTestApplication.class);

    public static void main(final String[] args) {
        SpringApplication.run(FileTestApplication.class, args);
    }

    @Override
    public void run(final String... args) throws Exception {
        LOG.info("EXECUTING : ...");

        final FlatFileItemReader<EmployeeModel> reader = new FlatFileItemReader<>();

        reader.setResource(new FileSystemResource("emp.csv"));
        reader.setLinesToSkip(1);
        reader.setLineMapper(lineMapper());
        reader.open(new ExecutionContext());

        EmployeeModel line;
        while((line = reader.read() )!= null) {
            System.out.println(line);
        }

        reader.close();

        LOG.info("APPLICATION FINISHED");
    }

    @Bean
    public LineMapper<EmployeeModel> lineMapper() {
        final DefaultLineMapper<EmployeeModel> defaultLineMapper = new DefaultLineMapper<>();

        final DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setDelimiter(";");
        lineTokenizer.setNames("id", "vorname", "nachname");
        defaultLineMapper.setLineTokenizer(lineTokenizer);

        //defaultLineMapper.setFieldSetMapper(new EmployeeFieldSetMapper()); // custom mapper, see above
        defaultLineMapper.setFieldSetMapper(fieldSetMapper());

        return defaultLineMapper;
    }

    @Bean
    public FieldSetMapper<EmployeeModel> fieldSetMapper() {
        final BeanWrapperFieldSetMapper<EmployeeModel> mapper = new BeanWrapperFieldSetMapper<>();
        mapper.setTargetType(EmployeeModel.class);
        return mapper;
    }
}
pom.xml
XML:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>file-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>file-test</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
emp.csv
Code:
0;alex;mueller
1;alex;mueller
2;alex;mueller
3;alex;mueller
 

Oneixee5

Top Contributor
Im oberen Beispiel kommt das noch nicht gut. Hier noch als Ergänzung:
Java:
@Configuration
public class FileTestConfiguration {

    @Bean
    public LineMapper<EmployeeModel> lineMapper() {
        final DefaultLineMapper<EmployeeModel> defaultLineMapper = new DefaultLineMapper<>();

        final DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setDelimiter(";");
        lineTokenizer.setNames("id", "vorname", "nachname");
        defaultLineMapper.setLineTokenizer(lineTokenizer);

        defaultLineMapper.setFieldSetMapper(fieldSetMapper());

        return defaultLineMapper;
    }

    @Bean
    public FieldSetMapper<EmployeeModel> fieldSetMapper() {
        final BeanWrapperFieldSetMapper<EmployeeModel> mapper = new BeanWrapperFieldSetMapper<>();
        mapper.setTargetType(EmployeeModel.class);
        return mapper;
    }

}
und entsprechend:
Java:
@SpringBootApplication
public class FileTestApplication implements CommandLineRunner {

    @Autowired
    private LineMapper<EmployeeModel> empMapper;

    public static void main(final String[] args) {
        SpringApplication.run(FileTestApplication.class, args);
    }

    @Override
    public void run(final String... args) throws Exception {
        LOG.info("EXECUTING : ...");

        final FlatFileItemReader<EmployeeModel> reader = new FlatFileItemReader<>();

        reader.setResource(new FileSystemResource("emp.csv"));
        reader.setLinesToSkip(1);
        reader.setLineMapper(this.empMapper);
        reader.open(new ExecutionContext());

        EmployeeModel line;
        while((line = reader.read() )!= null) {
            System.out.println(line);
        }

        reader.close();

        LOG.info("APPLICATION FINISHED");
    }

}
 

mrBrown

Super-Moderator
Mitarbeiter
Eine Webapp. Ich habe es bereits mit einem ExecutionContext probiert. Hat leider auch nicht funktioniert. Ich finde leider auch im Internet keine Antwort zu der Frage, ob es überhaupt ohne ein Batch möglich ist.
Wenn du Spring Batch nicht nutzen willst, warum dann Spring Batch?

Nur um CSVs zu lesen reicht auch was leichtgewichtigeres, zB Apache Commons, das funktioniert auch alles problemlos mit Spring
 

Alex_Wa

Mitglied
Wir verwenden auf der Arbeit Spring und im Laufe bin ich auf FlatFileItemReader gestoßen und wollte mal schauen wie es damit funktioniert. Habe einige Fehler gemacht und so habe ich was gelernt und kann diese Fehler beim nächsten Mal vermeiden.
 

Neue Themen


Oben