Menu

#82 AbstractMappingStrategy.findMaxFieldIndex() missing in 5.1 what is the replacement?

v1.0 (example)
wont-fix
5
2020-03-28
2020-03-06
Jason Reed
No

Hello,

So I've been using 4.5 and 4.6 for a lot of CSV handling for a project I've been working on for the last year or so. One of my requirements was to generate a CSV with specifically named columns that cannot be matched in the Java code and have them placed in a specific order, which I was able to do by extending the ColumnPositionMappingStrategy and overwriting the generateHeader method, and then using some reflection to read the column value in the @CsvBindByName annotation. It's worked fine for the most part. In my override method I would use the findMaxFieldIndex() method to get the number of fields so I can iterate through them.

I had found a possible bug where not all of my bean fields were identified and so my CSV had the last column with a missing column header, and I figured maybe to see if version 5.1 fixes this problem. So now my problem is that I would need to rewrite my mapping strategy class to deal with the changes in the BeanField class. I was surprised to find that there was now no way to get the number of fields. The findMaxFieldIndex() method is no longer present, and there doesn't seem to be a way to access the HeaderIndex object that is part of the AbstractMappingStrategy to check this value directly.

Is there another way to get this field count, or even a better mapping strategy that already handles both position and column name?

Discussion

  • Andrew Rucker Jones

    • assigned_to: Andrew Rucker Jones
     
  • Jason Reed

    Jason Reed - 2020-03-06

    Hi Thanks for the quick response.

    Sadly I don't think that is the solution I need. In my bean model for each field I use both @CsvBindByPosition and @CsvBindByName and since I'm extending the ColumnPositionMappingStrategy it will automatically put things in the right order, and I'm just reading out the @CsvBindByName value. The comparator system doesn't look like it would help me get the specifically defined header value. but rather perform a comparison to determine in what order things should be.

     
  • Andrew Rucker Jones

    I admit, I'm lost. I think this part of your requirement confuses me the most: "generate a CSV with specifically named columns that cannot be matched in the Java code". What does that mean?

     
  • Jason Reed

    Jason Reed - 2020-03-06

    Sorry I didn't make that clear enough. The column headers aren't very simple. They sometimes contain special characters, or spaces which don't work as field names in the java code. If I remember correctly it is the field names that what would be used if I only used the @CsvBindByPosition and ColumnPositionMappingStrategy by itself.

     
  • Andrew Rucker Jones

    CsvBindByPosition and ColumnPositionMappingStrategy don't use column names. However, opencsv has long been able to map an arbitrary column name to a Java field with CsvBindByName(column = "name in input file"). Then you can use the Comparator as detailed in the documentation I sent a link to to define the write order for your columns. You shouldn't need any of the position-related code.

    That's all assuming I now understand your use case.

     
  • Jason Reed

    Jason Reed - 2020-03-06

    I'm not sure I follow completely.

    If I use the CsvBindByPosition with ColumnPositionMappingStrategy only then I get a report with the data in the correct columns but no column headers. If I use CsvBindByName with HeaderColumnNameMappingStrategy only then I get the headers I need but they are not in the order in which I need them.

    So if I understand what you are saying there should be a way to perform a mapping so that my data with column headers can be written in the order in which I need them to be written, and I can do this using a comparator?

    Until this point here is how I've accomplished getting what I need using this code:

    public class ColumnCSVMapping<T> extends ColumnPositionMappingStrategy <T>
    {
    
        @Override
        public String[] generateHeader(final T bean) throws CsvRequiredFieldEmptyException
        {
            super.setColumnMapping(new String[FieldUtils.getAllFields(bean.getClass()).length]);
            final int numColumns = this.findMaxFieldIndex();
            if (!this.isAnnotationDriven() || (numColumns == -1))
            {
                return super.generateHeader(bean);
            }
    
            final String[] header = new String[numColumns];
            BeanField <T> beanField;
            for (int i = 0; i < numColumns; i++)
            {
    
                beanField = this.findField(i);
                final String columnHeaderName = this.extractHeaderName(beanField);
                header[i] = columnHeaderName;
            }
    
            return header;
    
        }
    
        private String extractHeaderName(final BeanField <T> beanField)
        {
            if ((beanField == null) || (beanField.getField() == null) || (beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0))
            {
                return StringUtils.EMPTY;
            }
    
            final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
            return bindByNameAnnotation.column();
        }
    
    }
    

    With a bean that looks like this:

    public class ReportModel implements Serializable
    {
        /**
         *
         */
        private static final long serialVersionUID = 7557569912345671813L;
        @CsvBindByPosition(position = 0)
        @CsvBindByName(column = "Doc-Type")
        private String docType = "";
        @CsvBindByPosition(position = 1)
        @CsvBindByName(column = "Verarbeitet j/n")
        private String verarbeitet = "";
        @CsvBindByPosition(position = 2)
        @CsvBindByName(column = "Doc-Nummer")
        private String docNummer = "";
        @CsvBindByPosition(position = 3)
        @CsvBindByName(column = "Doc-Datum")
        private String docDatum = "";
        @CsvBindByPosition(position = 4)
        @CsvBindByName(column = "Fehlercode")
        private String fehlerCode = "";
        @CsvBindByPosition(position = 5)
        @CsvBindByName(column = "Fehlertext")
        private String fehlerText = "";
    
        // Getters and setters below 
        }
    

    Does this help you understand my requirements better? I would want to recreate this functionality in 5.1 if possible.

     
    • Andrew Rucker Jones

      That's exactly what I'm saying, and it's exactly what the example code in the first link I sent does. You can get rid of your entire derived MappingStrategy and all of the CsvBindByPosition annotations and more or less copy the example code from the documentation.

       
  • Scott Conway

    Scott Conway - 2020-03-07

    Hello - Sorry for jumping in so late but was very busy at work this week and did not check emails.

    You said your column names have special characters and/or spaces - Have you tried the HeaderColumnNameTranslateMappingStrategy which allows you to pass in a map of column names and bean field names and it will map between the two. Or is the special characters or spaces causing that to fail.

    For getting the number of columns in the ColumnPostionMappingStrategy I believe you can call the getColumnMapping method which will return you the array of Strings that is normally the header but is the first line that was read from the CSVReader (needed because we are implenting the MappingStrategy and we use that as a way of verifying all the columns have the same number of fields. But that said your max field index is the length of that array - 1.

    Scott :)

     
    • Andrew Rucker Jones

      … but again, special characters and spaces are not a problem if one uses the annotations the way they were meant to be used, and counting columns is not necessary in order to get the desired column order on writing.

       
  • Andrew Rucker Jones

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

    Closed due to lack of activity.

     

Log in to post a comment.