春季批处理ItemReader语言环境,用逗号导入双精度

时间:2019-08-20 08:18:21

标签: spring-batch

我想使用Spring Batch导入以下文件

key;value
A;9,5

我用bean建模

class CsvModel
{
    String key
    Double value
}

此处显示的代码是Groovy,但是该语言与该问题无关。

@Bean
@StepScope
FlatFileItemReader<CsvModel> reader2()
{
    // set the locale for the tokenizer, but this doesn't solve the problem
    def locale = Locale.getDefault()
    def fieldSetFactory = new DefaultFieldSetFactory()
    fieldSetFactory.setNumberFormat(NumberFormat.getInstance(locale))

    def tokenizer = new DelimitedLineTokenizer(';')
    tokenizer.setNames([ 'key', 'value' ].toArray() as String[])

    // and assign the fieldSetFactory to the tokenizer
    tokenizer.setFieldSetFactory(fieldSetFactory)

    def fieldMapper = new BeanWrapperFieldSetMapper<CsvModel>()
    fieldMapper.setTargetType(CsvModel.class)

    def lineMapper = new DefaultLineMapper<CsvModel>()
    lineMapper.setLineTokenizer(tokenizer)
    lineMapper.setFieldSetMapper(fieldMapper)

    def reader = new FlatFileItemReader<CsvModel>()
    reader.setResource(new FileSystemResource('output/export.csv'))
    reader.setLinesToSkip(1)
    reader.setLineMapper(lineMapper)

    return reader
}

设置阅读器是众所周知的,对我来说,新功能是第一个代码块,设置一个numberFormat / locale / fieldSetFactory并将其分配给令牌生成器。但这不起作用,我仍然收到异常

Field error in object 'target' on field 'value': rejected value [5,0]; codes [typeMismatch.target.value,typeMismatch.value,typeMismatch.float,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.value,value]; arguments []; default message [value]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'float' for property 'value'; nested exception is java.lang.NumberFormatException: For input string: "9,5"]
    at org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper.mapFieldSet(BeanWrapperFieldSetMapper.java:200) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.item.file.mapping.DefaultLineMapper.mapLine(DefaultLineMapper.java:43) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:180) ~[spring-batch-infrastructure-4.1.2.RELEASE.jar:4.1.2.RELEASE]

所以问题是:如何在语言环境de_AT中导入浮点数(我们用类似这样的逗号写小数:3,141592)?我可以使用FieldSetMapper来避免此问题,但我想了解这里发生的事情,并希望避免不必要的mapper类。

甚至FieldSetMapper解决方案也不遵循开箱即用的语言环境,我必须阅读一个字符串,然后自己将其转换为双精度字符串:

class PnwExportFieldSetMapper implements FieldSetMapper<CsvModel>
{
    private nf = NumberFormat.getInstance(Locale.getDefault())

    @Override
    CsvModel mapFieldSet(FieldSet fieldSet) throws BindException
    {
        def model = new CsvModel()
        model.key = fieldSet.readString(0)
        model.value = nf.parse(fieldSet.readString(1)).doubleValue()
        return model
    }
}

DefaultFieldSet有一个函数setNumberFormat,但是我在何时何地调用此函数?

1 个答案:

答案 0 :(得分:2)

不幸的是,这似乎是一个错误。我有同样的问题,并调试到代码中。

使用DefaultFieldSetFactory的方法不是来实现BeanWrapperFieldSetMapper,该方法可以进行正确的转换,而仅使用FieldSet.getProperties并自行进行转换。

因此,我看到以下选项:为BeanWrapperFieldSetMapper提供PropertyEditor或ConversionService,或使用其他映射器。

以下是转换服务的示意图:

private static class CS implements ConversionService {

    @Override
    public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
        return sourceType == String.class && targetType == double.class;
    }

    @Override
    public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return sourceType.equals(TypeDescriptor.valueOf(String.class)) &&
               targetType.equals(TypeDescriptor.valueOf(double.class)) ;
    }

    @Override
    public <T> T convert(Object source, Class<T> targetType) {
        return (T)Double.valueOf(source.toString().replace(',', '.'));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        return Double.valueOf(source.toString().replace(',', '.'));
    }
}

并使用它:

      final BeanWrapperFieldSetMapper<IBISRecord> mapper = new BeanWrapperFieldSetMapper<>();
        mapper.setTargetType(YourClass.class);
        mapper.setConversionService(new CS());

...
new FlatFileItemReaderBuilder<IBISRecord>()
.name("YourReader")
                .delimited()
                .delimiter(";")
                .includedFields(fields)
                .names(names)
                .fieldSetMapper(mapper)
                .saveState(false)
                .resource(resource)
                .build();