我试图使用jackson-dataformat-csv解析一个CSV文件,并希望将数值列映射为Number java类型。
CsvSchema schema = CsvSchema.builder().setUseHeader(true)
.addColumn("firstName", CsvSchema.ColumnType.STRING)
.addColumn("lastName", CsvSchema.ColumnType.STRING)
.addColumn("age", CsvSchema.ColumnType.NUMBER)
.build();
CsvMapper csvMapper = new CsvMapper();
MappingIterator<Map<String, Object>> mappingIterator = csvMapper
.readerFor(Map.class)
.with(schema)
.readValues(is);
while (mappingIterator.hasNext()) {
Map<String, Object> entryMap = mappingIterator.next();
Number age = (Number) entryMap.get("age");
} 我希望entryMap.get("age")应该是Number,但我得到的是String。
我的CSV文件:
firstName,lastName,age
John,Doe,21
Error,Name,-10我知道CsvSchema可以很好地处理POJO,但是我需要处理任意的CSV模式,所以我不能为每一种情况创建一个新的java类。
将CSV解析为类型化Map或Array的任何方法
发布于 2019-03-13 02:22:44
您可以将单一性解析器用于这类事情。它更快,更灵活:
CsvParserSettingssettings = new CsvParserSettings(); //configure the parser if needed
CsvParser parser = new CsvParser(settings);
for (Record record : parser.iterateRecords(is)) {
Short age = record.getShort("age");
}要获得一个类型化的映射,请告诉解析器您正在使用的列的类型:
parser.getRecordMetadata().setTypeOfColumns(Short.class, "age" /*, and other column names*/);
//to get 0 instead of nulls when the field is empty in the file:
parser.getRecordMetadata().setDefaultValueOfColumns("0", "age", /*, and other column names*/);
// then parse
for (Record record : parser.iterateRecords(is)) {
Map<String,Object> map = record.toFieldMap();
}希望这能有所帮助
免责声明:我是这个图书馆的作者。它是开放源码和免费的(Apache2.0许可证)
发布于 2019-03-12 08:33:17
现在,不可能使用Map配置CsvSchema反序列化。流程使用com.fasterxml.jackson.databind.deser.std.MapDeserializer,它现在不检查架构。我们可以编写自定义的Map反序列化器。关于GitHub:CsvMapper在使用@JsonAnySetter时不尊重CsvSchema.ColumnType有一个问题,cowtowncoder回答了这个问题:
在这一点上,模式类型不常用于任何事情,但我同意它应该被使用。
编辑
我决定仔细看一看,在场景后面使用com.fasterxml.jackson.databind.deser.std.MapDeserializer这个事实,我们可以做些什么。实现自定义Map反序列化器(它将注意类型)将很难实现和注册,但我们可以使用有关ValueInstantiator的知识。让我们定义新的Map类型,它知道如何处理ColumnType信息:
class CsvMap extends HashMap<String, Object> {
private final CsvSchema schema;
private final NumberFormat numberFormat = NumberFormat.getInstance();
public CsvMap(CsvSchema schema) {
this.schema = schema;
}
@Override
public Object put(String key, Object value) {
value = convertIfNeeded(key, value);
return super.put(key, value);
}
private Object convertIfNeeded(String key, Object value) {
CsvSchema.Column column = schema.column(key);
if (column.getType() == CsvSchema.ColumnType.NUMBER) {
try {
return numberFormat.parse(value.toString());
} catch (ParseException e) {
// leave it as it is
}
}
return value;
}
}对于没有no-arg构造函数的新类型,我们应该创建新的ValueInstantiator
class CsvMapInstantiator extends ValueInstantiator.Base {
private final CsvSchema schema;
public CsvMapInstantiator(CsvSchema schema) {
super(CsvMap.class);
this.schema = schema;
}
@Override
public Object createUsingDefault(DeserializationContext ctxt) {
return new CsvMap(schema);
}
@Override
public boolean canCreateUsingDefault() {
return true;
}
}示例用法:
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.HashMap;
public class CsvApp {
public static void main(String[] args) throws IOException {
File csvFile = new File("./resource/test.csv").getAbsoluteFile();
CsvSchema schema = CsvSchema.builder()
.addColumn("firstName", CsvSchema.ColumnType.STRING)
.addColumn("lastName", CsvSchema.ColumnType.STRING)
.addColumn("age", CsvSchema.ColumnType.NUMBER)
.build().withHeader();
// Create schema aware map module
SimpleModule csvMapModule = new SimpleModule();
csvMapModule.addValueInstantiator(CsvMap.class, new CsvMapInstantiator(schema));
// register map
CsvMapper csvMapper = new CsvMapper();
csvMapper.registerModule(csvMapModule);
// get reader for CsvMap + schema
ObjectReader objectReaderWithSchema = csvMapper
.readerWithSchemaFor(CsvMap.class)
.with(schema);
MappingIterator<CsvMap> mappingIterator = objectReaderWithSchema.readValues(csvFile);
while (mappingIterator.hasNext()) {
CsvMap entryMap = mappingIterator.next();
Number age = (Number) entryMap.get("age");
System.out.println(age + " (" + age.getClass() + ")");
}
}
}以上代码用于以下CSV有效载荷:
firstName,lastName,age
John,Doe,21
Error,Name,-10.1指纹:
21 (class java.lang.Long)
-10.1 (class java.lang.Double)看上去像是黑客,但我想证明这种可能性。
https://stackoverflow.com/questions/55110124
复制相似问题