【问题标题】:Spring batch ItemReader locale, import a double with commaSpring批处理ItemReader语言环境,用逗号导入双精度
【发布时间】:2019-12-25 10:06:44
【问题描述】:

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

即使是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,但是我应该在何时何地调用这个函数?

【问题讨论】:

    标签: spring-batch


    【解决方案1】:

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

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

    因此,我看到了以下选项:为 BeanWrapperFieldSetMapper 提供 PropertyEditors 或 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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-13
      • 1970-01-01
      • 2021-05-09
      • 2018-05-27
      相关资源
      最近更新 更多