0

I've been working on a project where some new status data is fetched into an object and applied via ModelMapper on the current object status data. Fields are matched and updated automatically which is nice.

Now I've added some more complex data in the form of a List<Map<String, String>>.
And things are breaking.

Here is the simplified code :

public class TypeA {     
       private List<Map<String, String>> listOfMap;
      
}

public class TypeB {     
       private List<Map<String, String>> listOfMap;
      
}
 
public class Test {
       public static void main(String[] args) {           
        TypeA typeA = new TypeA();      
        TypeB typeB = new TypeB();
        
        Map<String, String>  m1 = new LinkedHashMap();
        m1.put("test1","machin");
        m1.put("test2","truc");
        m1.put("test3","bidule");
        
        Map<String, String>  m2 = new LinkedHashMap();
        m2.put("test1","machin");
        m2.put("test2","truc");
        
        List<Map<String, String>> l1 = new ArrayList<Map<String, String>>();        
        l1.add(m1);
        
        List<Map<String, String>> l2 = new ArrayList<Map<String, String>>();        
        l2.add(m2);
        
        typeA.setListOfMap(l1);     
        typeB.setListOfMap(l2);   // this can be set to null to fix the problem  
        
        ModelMapper modelmapper = new ModelMapper();
            
             try {
                    modelmapper.map(typeA, typeB); 
             } catch (Exception e) {
                    e.printStackTrace();
             }
       }
}

Things are working alright if typeB listOfMap is Empty.
But if we already have another list of values in listOfMap on object typeB.
Then we get the following error when trying to map A to B :

 
1) Converter org.modelmapper.internal.converter.MapConverter@1f32e575 failed to convert java.util.LinkedHashMap to java.util.LinkedHashMap.
 
1 error
       at org.modelmapper.internal.Errors.throwMappingExceptionIfErrorsExist(Errors.java:380)
       at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:81)
       at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:573)
       at org.modelmapper.ModelMapper.map(ModelMapper.java:447)
       at modelmapper_debug.Main.main(Main.java:30)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1
       at org.modelmapper.internal.converter.MapConverter.convert(MapConverter.java:59)
       at org.modelmapper.internal.converter.MapConverter.convert(MapConverter.java:1)
       at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
       at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
       at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
       at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:1)
       at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
       at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
       at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
       at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
       at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
       at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:115)
       at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:72)
       ... 3 more

What is happening here?
Can I fix this code to make ModelMapper map List<Map<String, String>> type correctly ?

thx all

Paul
  • 2,813
  • 2
  • 19
  • 12
  • So both typeA and typeB has same items in the list. While modelMapper is moving items from A to B, seems like it is erasing items from B to do fresh copy from A to B. So both A and B has ref to same item list. If modelMapper is erasing from one because of reference it will erase from first list too. Try to assign different lists to A and B. – smile Oct 04 '20 at 08:31
  • @smile I changed the code to use two different lists. It's the exact same problem. – Paul Oct 07 '20 at 14:42

2 Answers2

0
  1. I don't know why you use modelmapper.map(typeA, typeB) instead of modelmapper.map(typeA, TypeB.class) http://modelmapper.org/getting-started/
  2. You can use your own mappings for complex data:
public class TestModelMapper extends ModelMapper {

    TestModelMapper() {
        super();
        this.initMappings();
    }

    private void initMappings() {
        Converter>, List>> converter = new Converter() {
            public List> convert(MappingContext>, List>> context) {
                return context.getSource() == null ? null : new ArrayList(context.getSource());
            }
        };
        PropertyMap entityToDTOMap = new PropertyMap() {
            protected void configure() {
                using(converter).map(source.getListOfMap()).setListOfMap(null);
            }
        };

        this.addMappings(entityToDTOMap);
    }
}

public class Test {
    
    public static void main(String[] args) {           
        TypeA typeA = new TypeA();      
        TypeB typeB = new TypeB();
        
        Map  m1 = new LinkedHashMap();
        m1.put("test1","machin");
        m1.put("test2","truc");
        m1.put("test3","bidule");
        
        List> l1 = new ArrayList>();        
        l1.add(m1);
        
        typeA.setListOfMap(l1);     
        
        ModelMapper modelmapper = new TestModelMapper();
            
        try {
            modelmapper.map(typeA, TypeB.class); 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
0

At the time, I was running this code with modelmapper dependency version 2.3.8.
Updating to the latest release 2.4.2 fixes this problem.

    ModelMapper modelmapper = new ModelMapper();
        
    try {
        System.out.println(typeB.getListOfMap().toString());
        modelmapper.map(typeA, typeB); 
        System.out.println(typeB.getListOfMap().toString());
    } catch (Exception e) {
        e.printStackTrace();
    }

Ouputs as expected :

[{test1=machin, test2=truc}]  
[{test1=machin, test2=truc, test3=bidule}]
Paul
  • 2,813
  • 2
  • 19
  • 12