Search code examples
univocity

Validate parsed fields using Univocity Parser


I wanted to know if there is a way to check and validate a field when using the CsvRoutines package. Basically I want to process a row if the first column has only numbers and skip/possibly throw an exception otherwise. I'm guessing @Validate annotation released in 2.7.0 can be used to achieve this. But I would like to know if there is any other way to achieve the same with earlier versions like 2.5.9?


Solution

  • Author of the library here. There's no other way other than updating to the latest version. Is there any reason in particular why you can't upgrade?

    Update: you can put the @Parsed annotations on the class' getters or setters and perform the validations in them. That is probably the cleanest way to go about it. For example:

    class Test {
    
        private Integer number;
    
        //accepts a String here... so this is straight from the parser before it tries to convert anything into an integer - which lets you validate or throw a custom exception
        @Parsed
        void setNumber(String number){
            try{
                this.number = Integer.valueOf(number);
            } catch(NumberFormatException e){
                throw new IllegalArgumentException(number + " is not a valid integer");
            }
        }
    
    }
    

    Another alternative is to use a custom conversion class. Copy the code of class ValidatedConversion, used in the newest version, then create subclass like:

    public static class RangeLimiter extends ValidatedConversion {
        int min;
        int max;
    
        public RangeLimiter(String[] args) {
            super(false, false); //not null, not blank
            min = Integer.parseInt(args[0]);
            max = Integer.parseInt(args[1]);
        }
    
        protected void validate(Object value) {
            super.validate(value); //runs the existing validations for not null and not blank
            int v = ((Number) value).intValue();
            if (v < min || v > max) {
                throw new DataValidationException("out of range: " + min + " >= " + value + " <=" + max);
            }
        }
    }
    

    Now on your code, use this:

    @Parsed(field = "number")
    @Convert(conversionClass = RangeLimiter.class, args = {"1", "10"}) //min = 1, max = 10
    public int number;
    

    I didn't test this against an old version. I think you may need to set flag applyDefaultConversion=false in the @Parsed annotation, and make your conversion class convert a String into an int in addition to run the validations.

    All in all, that's quite a bit of work that can easily be avoided just by upgrading to the latest version.