Announcement Announcement Module
Collapse
No announcement yet.
FormatterLineAggregator trouble with null date formatting Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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.

  • #2
    The reason we transform null to empty string is that in the majority of cases, null should be represented in the output as blanks, not "null". Your case is special because you are using a date-based format. It would probably be easiest for you to create your own LineAggregator.

    Comment


    • #3
      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>

      Comment

      Working...
      X