In this example, I will show you how to read the date as String, parse it and format it to another format. I’ve used Spring XML based approach, however you can do the same with Spring Batch Annotation based approach.
pom.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.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>xml-spring-batch-csv-to-xml</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>xml-spring-batch-csv-to-xml</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Model
package com.example;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor(staticName = "create")
@NoArgsConstructor
@Data
@Builder
public class Employee {
private Long empId;
private String firstName;
private String lastName;
private Integer age;
private String email;
private String joiningDate;
}
The FieldSetMapper — Interface that is used to map data obtained from a FieldSet into an object.
package com.example;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class EmployeeFieldSetMapper implements FieldSetMapper<Employee> {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm:ss:SSSa", Locale.US);
DateTimeFormatter NEW_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd", Locale.US);
@Override
public Employee mapFieldSet(FieldSet fieldSet) throws BindException {
String joiningDate = fieldSet.readRawString("joiningDate");
return Employee.builder()
.empId(fieldSet.readLong("empId"))
.firstName(fieldSet.readString("firstName"))
.lastName(fieldSet.readString("lastName"))
.age(fieldSet.readInt("age"))
.email(fieldSet.readString("email"))
.joiningDate(joiningDate != null ? formatFDate(joiningDate) : null)
.build();
}
private String formatFDate(String value){
LocalDateTime dateTime = LocalDateTime.parse(value, formatter);
return dateTime.format(NEW_FORMAT);
}
}
xml context
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- Step will need a transaction manager -->
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
<batch:job id="employeeJob">
<batch:step id="step1">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="flatFileItemReader" writer="employeeWriter"
commit-interval="10"/>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" value="classpath:employee.csv" />
<property name="linesToSkip" value="1" />
<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.example.EmployeeFieldSetMapper" />
</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="delimiter" value=","/>
<property name="names" value="empId,firstName,lastName,age,email,joiningDate" />
</bean>
</property>
</bean>
</property>
</bean>
<bean id="employeeWriter" class="com.example.EmployeeItemWriter" />
</beans>
MainApp.java
package com.example;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MainApp implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MainApp.class, args);
}
@Override
public void run(String... args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-batch-context.xml");
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("employeeJob");
try {
JobExecution execution = jobLauncher.run(job, new JobParameters());
System.out.println("Job Exit Status : " + execution.getStatus());
} catch (JobExecutionException e) {
System.out.println("Job ExamResult failed");
e.printStackTrace();
}
}
}
Logs
2024-07-17 18:08:54.455 INFO 6382 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=employeeJob]] launched with the following parameters: [{}]
2024-07-17 18:08:54.475 INFO 6382 --- [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step1]
[Employee(empId=1, firstName=Jody, lastName=Yundt, age=37, email=Hang.Dicki@gmail.com, joiningDate=20240401)]
2024-07-17 18:08:54.515 INFO 6382 --- [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in 39ms
2024-07-17 18:08:54.520 INFO 6382 --- [ main] o.s.b.c.l.support.SimpleJobLauncher : Job: [FlowJob: [name=employeeJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 50ms
Job Exit Status : COMPLETED
employee.csv
empId,firstName,lastName,age,email,joiningDate
1,Jody,Yundt,37,Hang.Dicki@gmail.com,Apr 1 2024 12:00:00:000AM
- Reference —
https://mkyong.com/spring/spring-how-to-pass-a-date-into-bean-property-customdateeditor/
https://mkyong.com/spring-batch/how-to-convert-date-in-beanwrapperfieldsetmapper/