Java – Spring Batch – how to convert String from file to Date

javaspringspring-batch

I am trying to process a CSV file in which some of the fields are dates of the format "yyyy-MM-dd" – but the reader fails when it tries to convert the String from the CSV file to a Date in my model class.

The error is:

org.springframework.validation.BindException:
org.springframework.validation.BeanPropertyBindingResult: 1 error
Field error in object 'target' on field 'datetimeInactive': rejected
value [2011-04-27]; codes
[typeMismatch.target.datetimeInactive,typeMismatch.datetimeInactive,typeMismatch.java.util.Date,typeMismatch];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [target.datetimeInactive,datetimeInactive]; arguments [];
default message [datetimeInactive]]; default message [Failed to
convert property value of type 'java.lang.String' to required type
'java.util.Date' for property 'datetimeInactive'; nested exception is
java.lang.IllegalStateException: Cannot convert value of type
[java.lang.String] to required type [java.util.Date] for property
'datetimeInactive': no matching editors or conversion strategy found]

XML for the reader:

http://code.google.com/p/springbatch-in-action/source/browse/trunk/sbia/ch07/src/test/resources/com/manning/sbia/ch07/test-batch-reader-context.xml?r=145

In my XML config files I have the following beans:

  <bean id="dateEditor" class="org.springframework.beans.propertyeditors.CustomDateEditor">
    <constructor-arg>
      <bean class="java.text.SimpleDateFormat">
        <constructor-arg value="yyyy-MM-dd" />
      </bean>
    </constructor-arg>
    <constructor-arg value="true" />
  </bean>

  <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
      <map>
        <entry key="java.util.Date">
          <ref local="dateEditor" />
        </entry>
      </map>
    </property>
  </bean>

My questions are:

  1. I have defined a CustomDateEditor in my context – so why cannot Spring convert the String into Date?

  2. I have read that there is a newer way in Spring 3 (Converter ?) to get the conversion done. i.e. http://forum.springsource.org/showthread.php?108480-Register-TypeConverter-PropertyEditor-w-Spring-Batch — however, I could not find any example code to this in the Spring Batch documentation. Could you show here how to do it / point me out to some link?

UPDATE:

I've got an answer to question #2:

XML:

  <mvc:annotation-driven conversion-service="conversionService" />

  <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="my.project.StringToDate">
                <!-- org.springframework.binding.convert.converters.StringToDate DEFAULT_PATTERN = "yyyy-MM-dd" -->
                <property name="pattern" value="yyyy-MM-dd" />
            </bean>
        </set>
    </property>
  </bean>

Custom Converter:

package my.project;

import java.util.Date;

import org.springframework.core.convert.converter.Converter;

public class StringToDate extends org.springframework.binding.convert.converters.StringToDate implements Converter<String, Date> {

    public Date convert(String source) {

        Date date = null;

        try {
            date = (Date) convertSourceToTargetClass(getPattern(), getTargetClass());
        } catch (Exception e) {

        }

        return date;
    }

}

I am still looking for an answer to question #1. I.e., after setting the converter, I am still getting BindException during the batch task. From this forum thread, it looks like my code should have performed the conversion.

Stack trace is:

Caused by: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'target' on field 'datetimeInactive': rejected value [2011-04-27]; codes [typeMismatch.target.datetimeInactive,typeMismatch.datetimeInactive,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.datetimeInactive,datetimeInactive]; arguments []; default message [datetimeInactive]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'datetimeInactive'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'datetimeInactive': no matching editors or conversion strategy found]
Field error in object 'target' on field 'datetimeActive': rejected value [2011-04-27]; codes [typeMismatch.target.datetimeActive,typeMismatch.datetimeActive,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.datetimeActive,datetimeActive]; arguments []; default message [datetimeActive]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'datetimeActive'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'datetimeActive': no matching editors or conversion strategy found]
    at org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper.mapFieldSet(BeanWrapperFieldSetMapper.java:186)
    at org.springframework.batch.item.file.mapping.DefaultLineMapper.mapLine(DefaultLineMapper.java:42)
    at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:179)
    ... 45 more

Best Answer

your forum reference is for type conversion while building the application context and configuring the beans

take a look at the JavaDoc for the BeanWrapperFieldSetMapper

To customize the way that FieldSet values are converted to the desired type for injecting into the prototype there are several choices. You can inject PropertyEditor instances directly through the customEditors *property*, or you can override the createBinder(Object) and initBinder(DataBinder) methods, or you can provide a custom FieldSet implementation.

meaning you should inject your CustomDateEditor directly into the Mapper

Related Topic