9

I am new to Mapstruct. I have a scenario where, in my target object I have a java map with key value pair<String,String> and I have to fill this map using source objects inner object properties/data member values.

My code is something like bellow(dummy code):

public class Student {
    public String name;
    public String rollNo;
    public Map<String, String> marks;
}


public class ExamResult{

    public String stud_name;
    public String Stud_rollNo;
    public Marks marks;
}

public class Marks{
    public Integer English;
    public Integer Maths;
    public Integer Science;
}

How would I manually achieve the same thing like bellow:

Student target;
ExamResult source;
target.setName(source.stud_name);
target.setRollNo(source.Stud_RollNo);
target.marks.put("ENGLISH",source.marks.english_marks);
target.marks.put("MATHS",source.marks.math_marks);
target.marks.put("SCIENCE",source.marks.science_marks);

For direct property mapping I have found code, but not sure how I can map the values to be filled in the marks map.

I had given a thought to use java expression to fill in target map values, but didn't found any documentation or such example of expressions using for target object.

I was thinking to use like bellow but not sure it will work:

    @Mapping(source = "stud_name", target = "name")
    @Mapping(source = "Stud_RollNo", target = "rollNo")
    @Mapping(source = "source.marks.english_marks",target = "java( marks.put(\"ENGLISH\",source.marks.english_marks )")
    @Mapping(source = "source.marks.math_marks",target = "java( marks.put(\"MATHS\",source.marks.math_marks )")
    @Mapping(source = "source.marks.science_marks",target = "java( marks.put(\"SCIENCE\",source.marks.science_marks )")
Student doConvert(ExamResult src)

Any help, any suggestion or any workaround is appreciated. Thanks in Advance.

Anonymous
  • 1,726
  • 4
  • 22
  • 47

3 Answers3

6

Using expressions in target is not allowed for MapStruct, that's why you couldn't make it work.

Mapping into maps from objects is also not supported yet, we have different issues requesting this feature.

What I would suggest is to use the automatic mapping as much as possible when you can and then resort to @AfterMapping when MapStruct can't do that. So in your case something like:

@Mapper
public interface StudentMapper {

    @Mapping(source = "stud_name", target = "name")
    @Mapping(source = "Stud_RollNo", target = "rollNo")
    @Mapping(target = "marks", ignore = true) // mapped in @AfterMapping
    Student doConvert(ExamResult src)

    @AfterMapping
    default void addStudentMarks(ExamResult result, @MappingTarget Student student) {
        student.marks = new HashMap<>();
        student.marks.put("ENGLISH", result.marks.ENGLISH);
        student.marks.put("MATHS", result.marks.MATHS);
        student.marks.put("SCIENCE", result.marks.SCIENCE);
    }

}
Filip
  • 19,269
  • 7
  • 51
  • 60
0

I would write a Custom mapper method to convert the Marks into Map Object

@Mapping(source = "stud_name", target = "name")
@Mapping(source = "Stud_rollNo", target = "rollNo")
Student doConvert(ExamResult examResult);

static Map<String,String> mapMarks(Marks marks) {
    Map<String,String> marksMap = new HashMap<>();
    marksMap.put("ENGLISH", String.valueOf(marks.getEnglish()));
    marksMap.put("MATHS", String.valueOf(marks.getMaths()));
    return marksMap;
}

In case the map elements are too big to mention, Jackson Library could be used which can dynamically create the map with reference name as Key and Object value as value

@Mapping(source = "stud_name", target = "name")
@Mapping(source = "Stud_rollNo", target = "rollNo")
Student doConvert(ExamResult examResult);

ObjectMapper mapper = new ObjectMapper();
static Map<String,String> mapMarks(Marks marks) {
    return mapper.convertValue(marks, Map.class);
}
SKumar
  • 1,940
  • 1
  • 7
  • 12
0

what about this:


@Mapper
public interface MyMapper {

    @Mapping( target = "name", source = "stud_name")
    @Mapping( target = "rollNo", source = "stud_rollNo")
    // marks will be mapped based on name equality
    Student map( ExamResult result);

    // mapstruct sees an unmapped property (although it only has a getter), marks
    @Mapping( target = "marks", ignore = true )
    MarksWrapper toWrapper(Marks marks );

    // handwritten method, just to do the mapping
    default Map<String, Integer> toMap( MarksWrapper wrapper) {
        return wrapper.getMarks();
    }

    // this class does the conversion
    class MarksWrapper {

        private Map<String, Integer> marks = new HashMap<>(  );

        public void setEnglish( Integer mark) {
            marks.put( "ENGLISH", mark );
        }
        public void setMaths( Integer mark ){
            marks.put( "MATH", mark );
        }
        public void setScience( Integer mark ) {
            marks.put( "SCIENCE", mark );
        }
        public Map<String, Integer> getMarks() {
            return marks;
        }
    }
}

Note: I used a Map<String,Integer> Map<String,String> but the idea is the same..

Sjaak
  • 3,602
  • 17
  • 29