2015-09-24 5 views
4

Я пытаюсь прочитать csv и добавить поля в структуру данных. Но одна из строк не сформирована должным образом, и я это знаю. Я просто хочу пропустить строку и перейти к другой. Но, хотя я уловил исключение, он все еще прерывает цикл. Любая идея, что мне здесь не хватает?skip malformed csv row

Мой CSV:

"id","name","email" 
121212,"Steve","[email protected]" 
121212,"Steve","[email protected]",, 
121212,"Steve","[email protected]" 

Мой код:

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: [email protected]; 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) 
+0

Какое исключение выбрасывается? Измените свой вопрос, чтобы включить трассировку стека. – dsh

+0

за то, что стоит, во время моего исследования я обнаружил некоторые проблемы, которые могут быть связаны с проблемой, с которой я сталкиваюсь. https://github.com/FasterXML/jackson-dataformat-csv/issues/52 – notacyborg

ответ

1

С обработкой Jackson 2.6 readValues() было улучшено, чтобы попытаться восстановить ошибки при обработке, так что во многих случаях вы можете просто попробовать еще раз, чтобы прочитать следующие допустимые строки. Поэтому не забудьте использовать хотя бы версию 2.6.2.

Более ранние версии также не восстанавливались, как правило, остальная часть содержимого не обрабатывалась; это может произойти в вашем случае.

Еще одна возможность, учитывая, что ваша проблема связана не с недопустимым CSV, а с одним, не отображаемым как POJO (по крайней мере, как определено POJO), заключается в том, чтобы читать содержимое как последовательность из String[] и обрабатывать сопоставление вручную. Сам анализатор CSV от Jackson не против какого-либо количества столбцов, это привязка данных более высокого уровня, которая напоминает поиск «лишнего» контента, который он не распознает.

+0

Я попробовал обновить Jackson до 2.6.2, но это действительно не решило мою проблему. Однако использование String [] действительно работало. Благодарю. – notacyborg

+0

@notacyborg Хорошо, я, вероятно, напишу о проблеме, если правильно пойму вашу проблему. Определенно звучит как проблема, которая должна быть легко восстановлена. – StaxMan

+0

Спасибо, @StaxMan. Пожалуйста, напишите мне на вопрос, когда/если вы закончите его заполнение. Я хотел бы участвовать в дискуссиях. Это избавит меня от головной боли, когда оно будет исправлено. :) – notacyborg

1

com.fasterxml.jackson.core.JsonParseException является n IOException, чтобы исключение попадало в блок try-catch. Тот факт, что его не поймают, заставляет меня поверить, что это происходит в методе hasNext(). Это общий шаблон: для того, чтобы узнать, есть ли другое, вам действительно нужно попытаться прочитать следующий.

+0

Извините за укороченный стек. Я добавил некоторые подробности. – notacyborg

0

Я не могу сказать наверняка, так как некоторые из трассировки стека был опущен, однако:

  • Если ArrayIndexOutOfBoundsException исключение, которое выбрасывается (в отличие от того, чтобы быть "cause"), то причина в том, что вы поймать его за пределами вашей петли.
  • Если исключение является (подкласс) IOException, то, как писал Крис Геркен, он может быть брошен в it.hasNext(), и в этом случае вы его не поймаете, и ваша программа выйдет.

Остальная часть трассировки стека указывает, какая из этих или каких-либо других причин является проблемой.



Обновление на основе полного вывода и трассировки стека:

В строке 24 CSVTest.java, вы звоните .nextValue(). При реализации вызова этого метода вызывается JsonParseException. Поскольку это подкласс класса IOException, ваш блок catch поймает его, распечатает трассировку стека и продолжит цикл. Все идет нормально.

com.fasterxml.jackson.core.JsonParseException: Too many entries: expected at most 3 (value #3 (0 chars) "") 
at [Source: [email protected]; 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) 

После этого, в строке 21 CSVTest.java, вы звоните .hasNextValue(). При реализации этого метода выбрасывается ArrayIndexOutOfBoundsException. Вы его поймаете, а также распечатаете трассировку стека. Однако ваш блок 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) 

Если вы действительно хотите, чтобы продолжить цикл здесь, то вам нужно будет двигаться, что примерка поймать построить внутри цикла.Может быть, как это:

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. Похоже, что вы столкнулись с ограничением (или ошибкой) в библиотеке, когда дело доходит до пропуска неправильных строк.

+0

жаль об этом! Я добавил полную трассировку стека. – notacyborg

1

Ваш 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).