0

我想用 Spring Batch 导入以下文件

key;value
A;9,5

我用豆建模

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)?我可以用 a 来避免这个问题,FieldSetMapper但我想了解这里发生了什么,并想避免不必要的映射器类。

甚至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,但我何时何地调用此函数?

4

1 回答 1

3

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

BeanWrapperFieldSetMapper没有使用 DefaultFieldSetFactory 的方法来进行正确的转换,而是使用 FieldSet.getProperties 并自行进行转换。

因此,我看到以下选项: 使用 PropertyEditors 或 ConversionService 提供 BeanWrapperFieldSetMapper,或者使用不同的映射器。

这是转换服务的草图:

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();
于 2019-10-30T12:06:29.310 回答