6

我一直在尝试读取 csv 并将字段添加到数据结构中。但是,其中一排没有正确形成,我知道这一点。我只想跳过这一行并继续前进。但是,即使我发现了异常,它仍然在打破循环。知道我在这里缺少什么吗?

我的.csv:

"id","name","email"
121212,"Steve","steve@example.com"
121212,"Steve","steve2@example.com",,
121212,"Steve","steve@example.com"

我的代码:

import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;

public static void main(String[] args) throws Exception{
    Path path = Paths.get("list2.csv");
    CsvMapper mapper = new CsvMapper();
    CsvSchema schema = CsvSchema.emptySchema().withHeader();
    MappingIterator<Object> it = mapper.reader(Object.class)
            .with(schema)
            .readValues(path.toFile());

    try{
        while(it.hasNext()){
            Object row;
            try{
                row = it.nextValue();
            } catch (IOException e){
                e.printStackTrace();
                continue;
            }
        }
    } catch (ArrayIndexOutOfBoundsException e){
        e.printStackTrace();
    }

}

例外:

com.fasterxml.jackson.core.JsonParseException: Too many entries: expected at most 3 (value #3 (0 chars) "")
 at [Source: java.io.InputStreamReader@12b3519c; line: 3, column: 38]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1486)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:518)
    at com.fasterxml.jackson.dataformat.csv.CsvParser._handleNextEntryExpectEOL(CsvParser.java:601)
    at com.fasterxml.jackson.dataformat.csv.CsvParser._handleNextEntry(CsvParser.java:587)
    at com.fasterxml.jackson.dataformat.csv.CsvParser.nextToken(CsvParser.java:474)
    at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.mapObject(UntypedObjectDeserializer.java:592)
    at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserialize(UntypedObjectDeserializer.java:440)
    at com.fasterxml.jackson.databind.MappingIterator.nextValue(MappingIterator.java:188)
    at CSVTest.main(CSVTest.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
java.lang.ArrayIndexOutOfBoundsException: 3
    at com.fasterxml.jackson.dataformat.csv.CsvSchema.column(CsvSchema.java:941)
    at com.fasterxml.jackson.dataformat.csv.CsvParser._handleNamedValue(CsvParser.java:614)
    at com.fasterxml.jackson.dataformat.csv.CsvParser.nextToken(CsvParser.java:476)
    at com.fasterxml.jackson.databind.MappingIterator.hasNextValue(MappingIterator.java:158)
    at CSVTest.main(CSVTest.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
4

4 回答 4

2

您的 CSV 不一定格式不正确,事实上,具有不同列数的行是很常见的。

Univocity-parsers 可以毫无问题地处理这个问题。

最简单的方法是:

BeanListProcessor<TestBean> rowProcessor = new BeanListProcessor<TestBean>(TestBean.class);

CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.setRowProcessor(rowProcessor);
parserSettings.setHeaderExtractionEnabled(true);

CsvParser parser = new CsvParser(parserSettings);
parser.parse(new FileReader(Paths.get("list2.csv").toFile());

// The BeanListProcessor provides a list of objects extracted from the input.
List<TestBean> beans = rowProcessor.getBeans();

如果要丢弃使用列数不一致的行构建的元素,请覆盖该beanProcessed方法并使用该ParsingContext对象分析您的数据并决定是保留还是删除该行。

披露:我是这个库的作者。它是开源和免费的(Apache V2.0 许可证)。

于 2015-09-25T05:13:02.767 回答
2

在 Jackson 2.6 中,处理readValues()已得到改进,以尝试从处理错误中恢复,因此在许多情况下,您可以再试一次,以读取以下有效行。所以请确保至少使用 version 2.6.2

早期版本也没有恢复,通常会使其余内容无法处理;这可能就是您的情况。

另一种可能性,考虑到您的问题不是无效的 CSV,而是不可映射为 POJO(至少定义 POJO 的方式),是读取内容作为序列String[],并手动处理映射。Jackson 的 CSV 解析器本身并不介意任何数量的列,它是更高级别的数据绑定,它确实喜欢找到它无法识别的“额外”内容。

于 2015-09-25T20:58:44.307 回答
1

com.fasterxml.jackson.core.JsonParseException是一个IOException这样的异常应该在 try-catch 块中被捕获。它没有被捕获的事实使我相信它正在发生在hasNext()方法中。这是一个常见的模式:为了知道是否有另一个,你实际上必须尝试阅读下一个。

于 2015-09-24T13:14:19.620 回答
0

我不能确定,因为一些堆栈跟踪被省略了,但是:

  • 如果ArrayIndexOutOfBoundsException是抛出的异常(而不是“原因”),那么原因是您在循环之外捕获它。
  • 如果异常是 (subclass of) IOException,那么正如 Chris Gerken 所写,它可能会被抛出it.hasNext(),在这种情况下,您根本不会捕获它,因此您的程序将退出。

堆栈跟踪的其余部分将指示问题中的哪一个或其他原因。



基于完整输出和堆栈跟踪的更新:

在 CSVTest.java 的第 24 行,您调用.nextValue(). 在调用该方法的实现中,JsonParseException抛出了a。由于这是 的子类IOException,因此您的 catch 块会捕获它,打印堆栈跟踪并继续您的循环。到现在为止还挺好。

com.fasterxml.jackson.core.JsonParseException: Too many entries: expected at most 3 (value #3 (0 chars) "")
 at [Source: java.io.InputStreamReader@12b3519c; line: 3, column: 38]
   at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1486)
   at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:518)
   at com.fasterxml.jackson.dataformat.csv.CsvParser._handleNextEntryExpectEOL(CsvParser.java:601)
   at com.fasterxml.jackson.dataformat.csv.CsvParser._handleNextEntry(CsvParser.java:587)
   at com.fasterxml.jackson.dataformat.csv.CsvParser.nextToken(CsvParser.java:474)
   at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.mapObject(UntypedObjectDeserializer.java:592)
   at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserialize(UntypedObjectDeserializer.java:440)
   at com.fasterxml.jackson.databind.MappingIterator.nextValue(MappingIterator.java:188)
   at CSVTest.main(CSVTest.java:24)

之后,在 CSVTest.java 的第 21 行,调用.hasNextValue(). 在这个方法的实现中,ArrayIndexOutOfBoundsException抛出了an。你抓住它,并打印堆栈跟踪。但是,您的 catch 块位于循环之外,因此当您捕获异常时,循环已经退出。

java.lang.ArrayIndexOutOfBoundsException: 3
    at com.fasterxml.jackson.dataformat.csv.CsvSchema.column(CsvSchema.java:941)
    at com.fasterxml.jackson.dataformat.csv.CsvParser._handleNamedValue(CsvParser.java:614)
    at com.fasterxml.jackson.dataformat.csv.CsvParser.nextToken(CsvParser.java:476)
    at com.fasterxml.jackson.databind.MappingIterator.hasNextValue(MappingIterator.java:158)
    at CSVTest.main(CSVTest.java:21)

如果你真的想在这里继续你的循环,那么你需要在循环内移动那个 try-catch 结构。或许是这样的:

while (true)
    {
    try
        {
        if (!it.hasNextValue())
            { break; }
        }
    catch (final ArrayIndexOutOfBoundsException err)
        {
        err.printStackTrace();
        continue;
        }

    Object row;
    try
        { row = it.nextValue(); }
    catch (final IOException err)
        {
        err.printStackTrace();
        continue;
        }
    }

然而,这段代码是一个无限循环。当hasNextValue()抛出 ArrayIndexOutOfBoundsException 时,状态没有改变,循环永远不会结束。我展示这个是为了展示在循环内移动 catch 块的原理,而不是作为一个可行的解决方案。

您对引用 jackson-dataformat-csv 中错误处理的讨论的问题添加了评论。在跳过格式错误的行时,您似乎在库中遇到了限制(或错误)。

于 2015-09-24T13:24:22.743 回答