Menu

#172 java record support/value extraction

open
None
5
2025-09-21
2025-09-15
Kamil Wal
No

Hi, is there plan to add support out of the box ? I think that would be great addition.
I was doing some tests with custom mapping strategies that I wanted to add on my side without change request. Unfortunately currently populateNewBean defined in AbstractMappingStrategy by default calls no args constructor which won't work with records.
Hence my idea was to simply override populateNewBean, but there is one missing piece to make that work.
Maybe if you don't want to support records out of the box it would be possible to get access to most of the code in setFieldValue from AbstractBeanField ? Most notably getting value as that was only issue that I blocked me in implementing that on my own.

Discussion

  • Scott Conway

    Scott Conway - 2025-09-16
    • assigned_to: Scott Conway
     
  • Scott Conway

    Scott Conway - 2025-09-16

    Hello Kamil

    Sorry but there is no plans to support Java Records in opencsv. Java Records was added in Java 14 and standardized in Java 16 while we are committed to maintaining compatibility with Java 8.

    Now for the second part I am very happy to open access (as little as possible - Least Privlege and all that) to allow you to override as neccessary. But I would like a definitive list as to what you want opened and why. The reason I am being picky is your request actually confused me.... okay that shouldn't be a surprise I am easily confused. But both the populateNewBean in AbstractMappingStrategy and setFieldValue in AbstractBeanField are public so you can override them to your hearts content with your own implementation. Digging a little deeper into the setFieldValues there were only two method calls that were private and those were preProcessValue and validateValue which simply calls the processString or validate on an interface to a class the user (that is you) has to define so I see no reason to open that up - if anything if it was that important I figure you would come up with your own implementation and call it in your override of setFieldValue.

    So yes please send me a list of what needs to be opened up and how that would be helpful. As long as life is not raining down on me I can get those changes out pretty quickly. I just want to make sure there is a balance between extensibility, security and safety.

    Scott :)

     
  • Kamil Wal

    Kamil Wal - 2025-09-16

    Hi Scott, thank you for taking your time to look into my request.
    I fully understand why you don't want to add support for records due to compatibility reasons.

    In order to provide more details (and to make sure that my approach would work) I created this simple POC: https://github.com/Svanar/opencsv-record-strategy.
    Main logic part is in classes RecordColumnPositionMappingStrategy and RecordHeaderColumnNameMappingStrategy.

    The core functionality being method:

        private Object extractValue(
                BeanField<T, String> beanField,
                String value
        ) {
            Field field = beanField.getField();
            PreAssignmentProcessor[] processors = field.getAnnotationsByType(PreAssignmentProcessor.class);
    
            String fieldValue = value;
    
            for (PreAssignmentProcessor processor : processors) {
    //            fieldValue = preProcessValue(processor, fieldValue, field);
            }
    
            PreAssignmentValidator[] validators = field.getAnnotationsByType(PreAssignmentValidator.class);
    
            for (PreAssignmentValidator validator : validators) {
    //            validateValue(validator, fieldValue, field, beanField);
            }
    
            try {
                Method method = beanField.getClass().getDeclaredMethod("convert", String.class);
                method.setAccessible(true);
                return method.invoke(beanField, fieldValue);
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    

    As you can see I copied code from AbstractBeanField#setFieldValue class from opencsv.

    Crucial part is that reflective call to method convert that is currently hidden.
    From my perspective it would be great if BeanField had something along those lines:
    * Added new method like (I'm bad at naming methods) that will allow to implement my strategy without reflection. That is basically current implementation of setFieldValue, but public and does not actually set value.

    public final Object getConvertedCsvValue(Object bean, String value, String header) {
            if (required && StringUtils.isBlank(value)) {
                throw new CsvRequiredFieldEmptyException(
                        bean.getClass(), field,
                        String.format(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("required.field.empty"),
                                field.getName()));
            }
    
            PreAssignmentProcessor[] processors = field.getAnnotationsByType(PreAssignmentProcessor.class);
    
            String fieldValue = value;
    
            for (PreAssignmentProcessor processor : processors) {
                fieldValue = preProcessValue(processor, fieldValue);
            }
    
            PreAssignmentValidator[] validators = field.getAnnotationsByType(PreAssignmentValidator.class);
    
            for (PreAssignmentValidator validator : validators) {
                validateValue(validator, fieldValue);
            }
    
            return convert(fieldValue);
    }
    
    • Then actual setFieldValue would simply look like:
        @Override
        public final void setFieldValue(Object bean, String value, String header)
                throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException,
                CsvConstraintViolationException, CsvValidationException {
            Object fieldValue = getConvertedCsvValue(bean, value, header);
    
            assignValueToField(bean, fieldValue, header);
        }
    

    Ofcourse that is just my simplest idea and one that works for my use case.

    I hope now it is a bit clearer what I'm trying to achieve and what parts I'm missing.
    I'm happy to provide more details.

    Once again, thanks!
    Kamil

     
  • Scott Conway

    Scott Conway - 2025-09-20

    Kamil is your github project private because I am getting a 404 when I try and view it.

     
  • Kamil Wal

    Kamil Wal - 2025-09-20

    hi, I checked and it is public, however for some reason dot was added as part of url. This should work fine: https://github.com/Svanar/opencsv-record-strategy

     
  • Scott Conway

    Scott Conway - 2025-09-20

    convert is not hidden - it is an abstract method that is defined by whomever extends the AbstractBeanField.

    You can see examples of that in BeanFieldSplit, BeanFieldSingleValue, ConvertLanguageToBoolean.

    That said I can change preProcessValue and validateValue in AbstractBeanField from private to protected if you want to call them directly from the class you are using that extends the AbstractBeanField.

    Short term you can copy out the two methods and put them in your class. Or if you do not use the PreProcessors and PreValidators you can remove them entirely - we do not use them we created them to allow users of opencsv a way of injecting their own custom validations and string processors. I have defined a few preprocessors and validators but they are only used in unit tests.

    Scott :)

     
  • Kamil Wal

    Kamil Wal - 2025-09-21

    Yes, you are of course right, but maybe I haven't been precise enough. What I really wanted to say by hidden is that, my code cannot access that method from outside of the converter itself, but this is the method that gets the actual value that will be set in field. While I can override that method I can never call it by myself and that is problem in my approach to make records work.

    If you check mapping strategies that I created they are now using reflections to call the convert method in order to obtain the value of the srgument. As far as my understanding goes currently there is no method in BeanField to only get the value that would be set to the particular field, rather getting value is always followed by setting it in the field which does not work for records.
    This is the reason of my proposal to add new method to BeanField interface that will only convert value, but not set it.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.