FormatterLineAggregator trouble with null date formatting
Being a newbe in Spring-batch I'm facing a simple trouble when I try to generate a flat file with date formating.
Versions
JDK 1.5
spring-batch-infrastructure-2.0.4.RELEASE.jar
Context:
Generating a flat file with data extracted using a SQL query.
Some datas may be null (not mandatory)
Trouble:
When a date is null the formating can't be done it throws an exception :
Code:
IllegalFormatConversionException: Y != java.lang.String
Question:
Is there a way to cope with null dates when formatting?
Configuration:
Code:
<bean id="cleFormateur"
class="org.springframework.batch.item.file.transform.FormatterLineAggregator">
<property name="fieldExtractor">
<bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names"
value="id,soccle,matricule,blanc,codeBordereau,dateVali" />
</bean>
</property>
<property name="format" value="%09d%3.3s%-12.12s%6$tY-%6$tm-%6$td" />
</bean>
My opinion:
According to me this is due to ExtractorLineAggregator.aggregate method line 58 because this method replaces null values from the result of the FieldExtractor by an empty String.
Then the date formating can't be applied to a String.
Here is the Spring source code for ExtractorLineAggregator.aggregate:
Code:
/**
* Extract fields from the given item using the {@link FieldExtractor} and
* then aggregate them. Any null field returned by the extractor will be
* replaced by an empty String. Null items are not allowed.
*
* @see org.springframework.batch.item.file.transform.LineAggregator#aggregate(java.lang.Object)
*/
public String aggregate(T item) {
Assert.notNull(item);
Object[] fields = this.fieldExtractor.extract(item);
//
// Replace nulls with empty strings
//
Object[] args = new Object[fields.length];
for (int i = 0; i < fields.length; i++) {
if (fields[i] == null) {
args[i] = "";
}
else {
args[i] = fields[i];
}
}
return this.doAggregate(args);
}
In my opinion, processing the result of FieldExtractor is a nonsense. If the behaviour of the FieldExtractor is not correct, just let the user write another implementation.
workarraound using a custom FieldExtractor
I managed to find a workarround this way:
- Write a custom FieldExtractor
- This one has a Map associating for all Date or Calendar fields with theire formatting.
- It first convert non null fields to String
- Then delegates the formating.
Java
Code:
/**
* @author <b>(sylvain.mougenot) </b>
* @version 1.00, 9 déc. 2009
*/
public class BeanWrapperFieldExtractorDateFormatting<T>
implements FieldExtractor<T>, InitializingBean {
private String[] names;
private Map<String, String> dateFormatting;
/**
* @param names field names to be extracted by the {@link #extract(Object)} method.
*/
public void setNames(final String[] names) {
this.names = names;
}
/**
* @param dateFormatting the dateFormatting to set
*/
public void setDateFormatting(final Map<String, String> dateFormatting) {
this.dateFormatting = dateFormatting;
}
/* (non-Javadoc)
* @see org.springframework.batch.item.file.transform.FieldExtractor#extract(java.lang.Object)
*/
public Object[] extract(final T item) {
final List<Object> values = new ArrayList<Object>();
final BeanWrapper bw = new BeanWrapperImpl(item);
for (final String propertyName : this.names) {
Object propertyValue = bw.getPropertyValue(propertyName);
if (propertyValue != null) {
final String myFormat = dateFormatting.get(propertyName);
// Do we have a format
if (myFormat != null) {
// only format Date and Calendar Objects
if (propertyValue instanceof Date) {
propertyValue = DateFormatUtils.format((Date) propertyValue, myFormat);
} else if (propertyValue instanceof Calendar) {
propertyValue = DateFormatUtils.format(((Calendar) propertyValue).getTime(), myFormat);
}
}
}
values.add(propertyValue);
}
return values.toArray();
}
Bean config
Code:
<bean
class="org.springframework.batch.item.file.transform.FormatterLineAggregator">
<property name="fieldExtractor">
<bean
class="fr.mediapost.sirh.batch.distributeur.util.BeanWrapperFieldExtractorDateFormatting">
<!-- Bean de type DistributeurData -->
<property name="names"
value="dateEntreeSoci,typeContrat,dateSortieEtab,motifSortie,blanc,soccle,mtfcatehr,mtfcodehr" />
<property name="dateFormatting">
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="dateEntreeSoci" value="yyyy-MM-dd" />
<entry key="dateSortieEtab" value="yyyy-MM-dd" />
</map>
</property>
</bean>
</property>
<property name="format"
value="%10.10s%3.3S0001%10.10s%3.3S%3$10.10s%10sOO %3.3s%6.6S%6.6S%5$22sEMBAUC%2$-6.6S%5$10sDISTRIB%5$82s0%5$70s" />
</bean>