Menu

#90 convertValue from AbstractMappingStrategy disappear at 5.0

v1.0 (example)
wont-fix
None
5
2022-01-06
2021-04-07
No

hello !

i'm currently trying to modify the dependancy of opencsv from. 4.6 to at least 5.0.

An old developer of my project was overriding "convertValue" of AbstractMappingStrategy.

Unfortunatelly this method disappear in 5.0 , how I can do to use a custom "convertValue" ??

Thanks for your answer ;)

Discussion

  • Andrew Rucker Jones

    • assigned_to: Andrew Rucker Jones
     
  • Andrew Rucker Jones

    That depends very much on what you're trying to do.

    convertValue() was part of the Introspection code that we removed in favor of Reflection in 5.0. There is no direct analog in 5.0, I think.

     
  • Dimitri SCOLE

    Dimitri SCOLE - 2021-04-07

    We are implementing two classes using ColumnPositionMappingStrategy and HeaderColumnNameTranslateMappingStrategy .

    For both we overrided convertValues because we have some custom converters to apply for some specific columns .

    For exemple we have an "Image" column in our csv , we have multiple urls separated by | , and the goal of override this method was to extract and split these value and return a value Object (but in fact a List)

     
  • Andrew Rucker Jones

    Why not use CsvBindAndSplitByName or CsvBindAndSplitByPosition?

     
  • Andrew Rucker Jones

    • status: open --> closed
     
  • Andrew Rucker Jones

    Closed for lack of activity.

     
  • Dimitri SCOLE

    Dimitri SCOLE - 2021-09-14

    I reopen this ticket as i'm block to update 4.4 to 5.5 because convertValue is not available , there is definitely now way to override something like this ? :(

    I cannot use CsvBindAndSplitByName because we do some other transformations (not only split the result but some other transformations mapping etc...)

     
  • Andrew Rucker Jones

    • status: closed --> open
     
  • Andrew Rucker Jones

    We would have to know exactly what you're doing. I'm fairly well convinced that we've provided for every possible conversion need.

     
  • Dimitri SCOLE

    Dimitri SCOLE - 2021-11-27

    I think a code page is better than explanation :

    First we are using the parsing of open csv with two strategy (parse by column position or column names)

        CsvToBean<VehicleImport> csvToBean = new CsvToBeanBuilder<VehicleImport>(reader)
                    .withType(VehicleImport.class)
                    .withIgnoreLeadingWhiteSpace(true)
         **  .withMappingStrategy(csvConf.isPositionMapping()
                            ? new ColumnPositionStrategy(mapping, this)
                            : new ColumnNameStrategy(mapping, this))**
                    .withSeparator(csvConf.getDelimiterChar() != null
                            ? separatorService.get(csvConf.getDelimiterChar())
                            : CSVWriter.DEFAULT_SEPARATOR)
                    .withQuoteChar(csvConf.getQuoteChar() != null
                            ? csvConf.getQuoteChar().charAt(0)
                            : CSVWriter.NO_QUOTE_CHARACTER)
                    .withFilter(this::skipEmptyLines)
                    .withVerifier(this::verifyVehicle)
                    .build();
    

    We created a class who extends HeaderColumnNameTranslateMappingStrategy. after the value is parsed we need to apply automatically some converter that's we needed to replace

       protected Object convertValue(String value, PropertyDescriptor prop) throws InstantiationException, IllegalAccessException {
            PropertyEditor editor = this.getPropertyEditor(prop);
            Object obj = value;
            if (null != editor) {
                synchronized(editor) {
                    editor.setAsText(value);
                    obj = editor.getValue();
                }
            }
    
            return obj;
        }
    

    by

    @Override
    protected Object convertValue(String value, PropertyDescriptor prop) {
        return parser.getValue(value, prop);
    }
    

    }

    our method do this thing

       @Override
        public Object getValue(Object value, PropertyDescriptor prop) {
            if (value == null || StringUtils.isEmpty(value.toString())) {
                return null;
            }
            Object newValue = value;
            ValueConverter converter = getConverter(prop);
            if (converter != null) {
                newValue = converter.convert(value.toString(), prop);
            }
            return propertyConverterService.convertProperty(newValue, prop);
        }
    
    public class ColumnNameStrategy extends HeaderColumnNameTranslateMappingStrategy<VehicleImport> {
    
        private VehicleParser parser;
    
        public ColumnNameStrategy(InventoryImportMapping mapping, VehicleParser parser) {
    
            this.parser = parser;
    
            setType(VehicleImport.class);
    
            Map<String, String> columnMapping = mapping.getOptions()
                    .stream()
                    .filter(o -> o.getSourceInput() != null && o.getTargetInput() != null)
                    .collect(Collectors.toMap(InventoryImportMappingOption::getSourceInput, InventoryImportMappingOption::getTargetInput, (o1, o2) -> o1));
    
            setColumnMapping(columnMapping);
        }
    
    //no compatible with open csv 5.0 , 5.5
    **    @Override
        protected Object convertValue(String value, PropertyDescriptor prop) {
            return parser.getValue(value, prop);
        }
    }**
    
     
  • Decebal Suiu

    Decebal Suiu - 2021-12-07

    I have the same problem (missing convertValue method).
    My problem is that I have a bean (POJO) and multiple CSV providers. Each provider comes with different format for a ZonedDateTime field from bean (it's only an example).
    My impression is that version 5 comes to improve the part with annotation but unfortunately not everything comes down to annotations. I need a method to achieve the same result programatically, without to use annotations (converter in my particular example).

    HeaderColumnNameTranslateMappingStrategy<HistoricalQuote> strategy = new HeaderColumnNameTranslateMappingStrategy<>() {
    
        @Override
        protected Object convertValue(String value, PropertyDescriptor prop) throws InstantiationException, IllegalAccessException {
            // https://stackoverflow.com/questions/8317413/how-to-parse-non-string-values-in-opencsv-headercolumnnamemappingstrategy/12414830
            if ("date".equals(prop.getName())) {
                // https://stackoverflow.com/a/49232011
                LocalDateTime dateTime = LocalDateTime.of(LocalDate.parse(value, dateFormatter), LocalTime.of(0,0));
                return dateTime.atZone(ZoneId.systemDefault());
            }
    
            return super.convertValue(value, prop);
        }
    
    };
    
     

    Last edit: Decebal Suiu 2021-12-07
    • Andrew Rucker Jones

      I hear you that annotations aren't the solution to everything, and I agree. Ideally, we would have the option to specifcy bean bindings through an XML configuration file. I'm not sure I still have the energy to implement that, though.

      I can say this: if you are able to use annotations, your problem is easily solved starting in version 5.4. Use the "profiles" attribute to CsvDate, then pass the profile you want to CsvToBeanBuilder when you parse.

       
  • Decebal Suiu

    Decebal Suiu - 2021-12-08

    In the end I solved the problem with an override of determineConverter:

    HeaderColumnNameTranslateMappingStrategy<HistoricalQuote> strategy = new HeaderColumnNameTranslateMappingStrategy<>() {
    
        @Override
        protected CsvConverter determineConverter(Field field, Class<?> elementType, String locale, String writeLocale, Class<? extends AbstractCsvConverter> customConverter) throws CsvBadConverterException {
            if ("date".equals(field.getName())) {
                ZonedDateTimeConverter converter = new ZonedDateTimeConverter(dateFormatter);
                converter.setType(elementType);
                converter.setLocale(locale);
                converter.setWriteLocale(writeLocale);
                converter.setErrorLocale(errorLocale);
    
                return converter;
           }
    
            return super.determineConverter(field, elementType, locale, writeLocale, customConverter);
        }
    
    };
    

    I don't know if it's the right solution but I am satisfied by this solution.

     
  • Andrew Rucker Jones

    • status: open --> wont-fix
     
  • Andrew Rucker Jones

    User implemented workaround.

     

Log in to post a comment.

MongoDB Logo MongoDB