@Validate не пропускает недопустимые строки при использовании с CsvRoutines в синтаксическом анализаторе UniVocity.

Я использую парсер Univocity версии 2.7.3. У меня есть CSV-файл с 1 миллионом записей, который может вырасти в будущем. Я читаю только несколько конкретных столбцов из файла, и ниже приведены мои требования:

  • НЕ сохраняйте содержимое CSV в памяти в любой момент

  • Игнорировать/пропускать создание bean-компонента, если столбцы широты или долготы в CSV являются нулевыми/пустыми

Чтобы выполнить эти требования, я попытался реализовать CsvRoutines, чтобы данные CSV не копировались в память. Я использую аннотацию @Validate для полей «Широта» и «Долгота» и использовал обработчик ошибок, чтобы не возвращать какие-либо исключения, чтобы запись была пропущена при сбое проверки.

Пример CSV:

#version:1.0
#timestamp:2017-05-29T23:22:22.320Z
#brand:test report    
    network_name,location_name,location_category,location_address,location_zipcode,location_phone_number,location_latitude,location_longitude,location_city,location_state_name,location_state_abbreviation,location_country,location_country_code,pricing_type,wep_key
    "1 Free WiFi","Test Restaurant","Cafe / Restaurant","Marktplatz 18","1233","+41 263 34 05","1212.15","7.51","Basel","test","BE","India","DE","premium",""
    "2 Free WiFi","Test Restaurant","Cafe / Restaurant","Zufikerstrasse 1","1111","+41 631 60 00","11.354","8.12","Bremgarten","test","AG","China","CH","premium",""
    "3 Free WiFi","Test Restaurant","Cafe / Restaurant","Chemin de la Fontaine 10","1260","+41 22 361 69","12.34","11.23","Nyon","Vaud","VD","Switzerland","CH","premium",""
    "!.oist*~","HoistGroup Office","Office","Chemin de I Etang","CH-1211","","","","test","test","GE","Switzerland","CH","premium",""
    "test","tess's Takashiro","Cafe / Restaurant","Test 1-10","870-01","097-55-1808","","","Oita","Oita","OITA","Japan","JP","premium","1234B"

TestDTO.java

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class TestDTO implements Serializable {

    @Parsed(field = "location_name")
    private String name;
    @Parsed(field = "location_address")
    private String addressLine1;
    @Parsed(field = "location_city")
    private String city;
    @Parsed(field = "location_state_abbreviation")
    private String state;
    @Parsed(field = "location_country_code")
    private String country;
    @Parsed(field = "location_zipcode")
    private String postalCode;

    @Parsed(field = "location_latitude")
    @Validate
    private Double latitude;

    @Parsed(field = "location_longitude")
    @Validate
    private Double longitude;

    @Parsed(field = "network_name")
    private String ssid;
}

Main.java

 CsvParserSettings parserSettings = new CsvParserSettings();        
        parserSettings.detectFormatAutomatically();
        parserSettings.setLineSeparatorDetectionEnabled(true);
        parserSettings.setHeaderExtractionEnabled(true);
        parserSettings.setSkipEmptyLines(true);
        parserSettings.selectFields("network_name", "location_name","location_address", "location_zipcode",
                "location_latitude", "location_longitude", "location_city","location_state_abbreviation", "location_country_code");

        parserSettings.setProcessorErrorHandler(new RowProcessorErrorHandler() {
            @Override
            public void handleError(DataProcessingException error, Object[] inputRow, ParsingContext context) {
                //do nothing
            }
        });


        CsvRoutines parser = new CsvRoutines(parserSettings);
        ResultIterator<TestDTO, ParsingContext> iterator = parser.iterate(TestDTO.class, new FileReader("c:\\users\\...\\test.csv")).iterator();


        int i=0;
        while(iterator.hasNext()) {
            TestDTO dto = iterator.next();
            if(dto.getLongitude() == null || dto.getLatitude() == null)
                i++;            
        }

        System.out.println("count=="+i);

Проблема:

На самом деле я ожидал, что счетчик будет равен нулю, так как я добавил обработчик ошибок и не отбросил исключение проверки данных, но, похоже, это не так. Я думал, что @Validate вернет исключение, когда встретит запись с широтой или долготой как нулевую (оба столбца также могут быть нулевыми в одной и той же записи), которая обрабатывается и игнорируется/пропускается в обработчике ошибок.

По сути, я не хочу, чтобы UniVocity создавал и отображал ненужные объекты DTO в куче (и приводил к нехватке памяти), поскольку есть вероятность, что входящий файл CSV может иметь более 200 или 300 тыс. записей с широтой/долготой как ноль.

Я даже пытался добавить собственный валидатор в @Validate, но тщетно.

Может кто-нибудь, пожалуйста, дайте мне знать, что мне здесь не хватает?


person Venkatesh Nachimuthu    schedule 16.12.2018    source источник


Ответы (1)


Автор библиотеки здесь. Вы все делаете правильно. Это ошибка, и я только что открыл эту проблему здесь, чтобы решить ее сегодня.

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

В вашем случае просто добавьте следующую строку кода, и все будет работать нормально:

parserSettings.setColumnReorderingEnabled(false);

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

Вам также нужно будет проверить null в бите итерации:

TestDTO dto = iterator.next();
if(dto != null) { // dto may come null here due to validation
    if (dto.longitude == null || dto.latitude == null)
        i++;
    }
}

Надеюсь, это поможет, и спасибо за использование наших парсеров!

person Jeronimo Backes    schedule 17.12.2018
comment
К сожалению, установка для переупорядочения столбцов значения false не помогла. Я все еще вижу счет как 2. Я также не добавлял предложенную нулевую проверку для объекта dto и не получал NPE. Я все еще использую тот же код, что и в исходном вопросе. - person Venkatesh Nachimuthu; 17.12.2018
comment
Возможно, попробуйте последнюю версию: 2.7.6. - person Jeronimo Backes; 17.12.2018
comment
Да, кажется, работает с 2.7.6. Большое спасибо!! - person Venkatesh Nachimuthu; 17.12.2018