Java Jackson能否解析不同行具有不同模式的CSV文件?

Java Jackson能否解析不同行具有不同模式的CSV文件?,java,parsing,csv,jackson,Java,Parsing,Csv,Jackson,当文件的不同行具有不同的模式时,是否可以使用JacksonJackson dataformat CSV库(CsvSchema,CsvMapper等)有效解析CSV文件 我强调高效,因为我需要解析非常大的文件(>100000000行),并且应用程序对性能非常敏感。如果为每行中的每一列实例化了一个新的对象/字符串,则GC将拒绝我。我希望尽可能地将原语(例如,31返回为int) 如果是,建议的方法是什么 仅供参考,文件架构如下:ROW\u TYPE |……。也就是说,每行的第一列表示列类型,对于给定的

当文件的不同行具有不同的模式时,是否可以使用Jackson
Jackson dataformat CSV
库(
CsvSchema
CsvMapper
等)有效解析CSV文件

我强调高效,因为我需要解析非常大的文件(>100000000行),并且应用程序对性能非常敏感。如果为每行中的每一列实例化了一个新的
对象
/
字符串
,则GC将拒绝我。我希望尽可能地将原语(例如,
31
返回为
int

如果是,建议的方法是什么

仅供参考,文件架构如下:
ROW\u TYPE |……
。也就是说,每行的第一列表示列类型,对于给定的列类型,模式总是相同的。然后,行之间的其余列会有所不同,这取决于它们的列类型。例如:

1|"text1a"|2|3|4|true|"text2a"
2|3|"text"
1|"text1b"|5|6|7|false|"text2b"
目前我使用的是
neo4j csv

<dependency>
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-csv</artifactId>
    <version>2.2-SNAPSHOT</version>
</dependency>

我考虑切换的原因是我想减少项目依赖性,并且,因为我已经将Jackson用于JSON,所以将其用于CSV(性能/功能待定)是有意义的。尽管Jackson不支持按行在
CsvSchema
s之间进行自动切换(这意味着您需要进行两阶段处理;首先读取或绑定为
String[]
,然后使用
ObjectMapper.convertValue()
),可能会使用现有的多态反序列化支持。这将依赖于列命名的一些共性,因此我不知道这是否现实

假设它可以工作,您将需要一个基类,该基类的属性和要使用的第一列的逻辑名称相匹配;然后是具有类似匹配属性名称的子类型。 您可以在基类上使用
@JsonTypeInfo
,并使用'name'作为类型id;在子类上使用
@JsonTypeName
,或者使用
@JsonSubTypes
注释从基类引用。 也就是说,使用通常的Jackson配置


如果这不起作用,两阶段处理可能不是一个坏的选择。它会导致所有单元格值都被读取为不同的对象,但只要它们不被保留(即,您只在内存中保留一行数据),短期垃圾通常对GC来说没有问题(长期垃圾是昂贵的一种).

谢谢@StaxMan,但我不确定这是否合适。使用我现在使用的
charseek
解析器,我在1个阶段中进行行相关解析。我将此方法与regex split()进行了比较,即返回一个字符串数组,然后解析这些字符串。生成的垃圾量高出约100倍。每+a字符串[]的每一列对应1个字符串+无论字符串被解析成什么。这导致了大约6倍的性能下降和不可预测的GC行为-当你以>>1000000个对象/秒的速度生成>>10000000个对象时,GC需要对它们做些什么,即使它们很短livedRegex可能会有更高的开销。但是我想我只是建议检查基本读数的快/慢程度,如
String[]
每行都是。基于我所做的基准测试,CSV后端与JSON相比似乎没有不合理的开销,这就是为什么我会惊讶地看到任何超过2倍差异的因素。此外:我绝对不会构建一个大的行数组,保留这一点——这就是我使用流媒体的意思。如果保留所有行,对于昂贵的GCs,字符串必然以OldGen结尾。因此,这可以很容易地解释差异。Jackson总是以增量方式(逐块)读取内容,因此内存保留是正常的。当您编写“2x差异系数”时你在比较哪两件事?谢谢!看到高性能的实现和解决类似问题的各种方法总是很有趣的。
// do once per file
CharSeeker charSeeker = new BufferedCharSeeker(...), bufferSize);
int columnDelimiter = '|';
Extractors extractors = new Extractors();
Mark mark = new Mark();

// do repeatedly while parsing
charSeeker.seek(mark, columnDelimiters))
int eventType = charSeeker.extract(mark, extractors.int_()).intValue();

switch (eventType) {
    case 1: // parse row type 1
            break;
    case 2: // parse row type 2
            break;
...
...
}