Spring Batch: анализ файла CSV с помощью quoteCharacter

Я новичок в Spring Batch, мы знаем, что CSV-файлы бывают самых разных форм и форм… и некоторые из них синтаксически неверны. Я пытаюсь проанализировать файл CSV, эта строка начинается с '"' и заканчивайте '"'это мой CSV:

"1;Paris;13/4/1992;16/7/2006"
"2;Lyon;31/5/1993;1/8/2009"
"3;Metz;21/4/1990;27/4/2010"

Я пробовал это:

  <bean id = "itemReader" class = "org.springframework.batch.item.file.FlatFileItemReader">
    <property name = "resource" value = "data-1.txt" />
    <property name = "lineMapper">
      <bean class = "org.springframework.batch.item.file.mapping.DefaultLineMapper">
        <property name = "fieldSetMapper">
          <!-- Mapper which maps each individual items in a record to properties in POJO -->
          <bean class = "com.sam.fourthTp.MyFieldSetMapper" />
        </property>
        <property name = "lineTokenizer">
          <!-- A tokenizer class to be used when items in input record are separated by specific characters -->
          <bean class = "org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
            <property name = "quoteCharacter" value = "&quot;" />
            <property name = "delimiter" value = ";" />
          </bean>
        </property>
      </bean>
    </property>
  </bean>

Но это работает, когда файл CSV выглядит следующим образом:

"1";"Paris";"13/4/1992";"16/7/2006"
"2;"Lyon";"31/5/1993";"1/8/2009"
"3";"Metz";"21/4/1990";"27/4/2010"

Мой вопрос в том, как я могу разобрать свой CSV, когда строка начинается с '"' и закончить на '"' ??!

Вы пытались очистить резюме, удалив "?

Angelo Immediata 17.04.2019 18:35
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
808
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

quoteCharacter, как вы упомянули, применимо к полям, а не к записям.

My question is how I can parse my CSV when a line start with '"' and end with '"' ??!

Что вы можете сделать, это:

  • Читать строки как необработанные строки
  • Используйте обработчик составных элементов с двумя делегатами: один обрезает " с начала/конца каждой записи, а другой анализирует строку и сопоставляет ее с вашим доменным объектом.

Вот краткий пример:

import java.util.Arrays;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.item.support.CompositeItemProcessor;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class MyJob {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Bean
    public ItemReader<String> itemReader() {
        return new ListItemReader<>(Arrays.asList(
                "\"1;Paris;13/4/1992;16/7/2006\"",
                "\"2;Lyon;31/5/1993;1/8/2009\"",
                "\"3;Metz;21/4/1990;27/4/2010\"",
                "\"4;Lille;21/4/1980;27/4/2011\""
                ));
    }

    @Bean
    public ItemProcessor<String, String> itemProcessor1() {
        return item -> item.substring(1, item.length() - 1);
    }

    @Bean
    public ItemProcessor<String, Record> itemProcessor2() {
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setNames("id", "ville");
        lineTokenizer.setDelimiter(";");
        lineTokenizer.setStrict(false);
        BeanWrapperFieldSetMapper<Record> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(Record.class);
        return item -> {
            FieldSet tokens = lineTokenizer.tokenize(item);
            return fieldSetMapper.mapFieldSet(tokens);
        };
    }

    @Bean
    public ItemWriter<Record> itemWriter() {
        return items -> {
            for (Record item : items) {
                System.out.println(item);
            }
        };
    }

    @Bean
    public CompositeItemProcessor<String, Record> compositeItemProcessor() {
        CompositeItemProcessor<String, Record> compositeItemProcessor = new CompositeItemProcessor<>();
        compositeItemProcessor.setDelegates(Arrays.asList(itemProcessor1(), itemProcessor2()));
        return compositeItemProcessor;
    }

    @Bean
    public Step step() {
        return steps.get("step")
                .<String, Record>chunk(2)
                .reader(itemReader())
                .processor(compositeItemProcessor())
                .writer(itemWriter())
                .build();
    }

    @Bean
    public Job job() {
        return jobs.get("job")
                .start(step())
                .build();
    }

    public static class Record {

        private int id;
        private String ville;

        public Record() {
        }

        public int getId() {
            return id;
        }

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

        public String getVille() {
            return ville;
        }

        public void setVille(String ville) {
            this.ville = ville;
        }

        @Override
        public String toString() {
            return "Record{" +
                    "id = " + id +
                    ", ville='" + ville + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        jobLauncher.run(job, new JobParameters());
    }

}

Я использовал простой POJO под названием Record и сопоставил только два поля. Этот образец печатает:

Record{id=1, ville='Paris'}
Record{id=2, ville='Lyon'}
Record{id=3, ville='Metz'}
Record{id=4, ville='Lille'}

Надеюсь это поможет.

Привет, Махмуд :), спасибо за ваш ответ, я попробовал ваш код и получил это: @Bean method ScopeConfiguration.jobScope is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.

abdallah 18.04.2019 10:13

Это предупреждение было исправлено в Spring Batch 3.0.9/4.0.1. См. ПАРТИЯ-2161.

Mahmoud Ben Hassine 18.04.2019 11:02

Еще раз спасибо :D, но теперь я понял :/ :Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myJob': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.batch.core.configuration.annotation.JobB‌​uilderFactory com.drihem.sam.fourthTp.MyJob.jobs; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jobBuilders'

abdallah 18.04.2019 11:25

какую весеннюю пакетную версию вы используете?

Mahmoud Ben Hassine 18.04.2019 12:00

@Mohmoud еще раз спасибо за ваш ответ, который я использую: spring-batch-core 4.0.1.RELEASE

abdallah 18.04.2019 12:03

что с датой? "1;Paris;13/4/1992;16/7/2006"

abdallah 18.04.2019 12:38

чтобы получить даты, я добавил этот метод: public User mapFieldSet(FieldSet fieldSet) throws BindException { User result = new User(); result.setId(fieldSet.readInt(0)); result.setVille(fieldSet.readRawString(1)); result.setDateOfBirth(new LocalDate(fieldSet.readDate(2,"dd/MM/yyyy"))); result.setDateFirstDec(new LocalDate(fieldSet.readDate(2,"dd/MM/yyyy"))); return result; } еще раз спасибо!

abdallah 18.04.2019 15:10

в вашем коде вы используете Arrays мой вопрос: как я могу использовать файл .CSV?

abdallah 18.04.2019 16:58

Это просто пример, так как я не могу загрузить файл csv в ответ. Вы можете изменить читатель с помощью FlatFileItemReader, и он должен работать таким же образом. Самый важный момент касается процессора составных предметов.

Mahmoud Ben Hassine 18.04.2019 18:10

Привет, Махмуд, еще раз спасибо; Я делаю это как вопрос, не могли бы вы объяснить этот здесь, пожалуйста?

abdallah 18.04.2019 18:14

если я пользователь FlatFileItemReader, мой @Bean вернет считыватель типа String? мне нужно больше деталей, пожалуйста

abdallah 19.04.2019 09:49

Да, он должен возвращать строку, чтобы первый процессор мог обрезать начальные/конечные " символы. Я ответил на ваш другой вопрос stackoverflow.com/questions/55749966/… примером.

Mahmoud Ben Hassine 19.04.2019 09:53

Давайте продолжить обсуждение в чате.

abdallah 19.04.2019 17:09

Привет @MahmoudBenHassine, я хочу создать класс моего ItemProcessor public class MyItemProcessorTwo implements ItemProcessor<String , User> { @Override public User process(String item) throws Exception {DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer() lineTokenizer.setNames("id", "ville", "dateOfBirth", "dateFirstDec"); lineTokenizer.setDelimiter(";"); return item -> { FieldSet tokens = lineTokenizer.tokenize(item); return mapFieldSet(tokens); };} }, но я получил это The target type of this expression must be a functional interface в item ->

abdallah 26.04.2019 11:58

Другие вопросы по теме