Mapstruct: HashMap as source to Object
Asked Answered
C

3

9

How could I use a HashMap<String, Object> as source to an object?

Here is my target object:

public class ComponentStyleDTO{
    private String attribute;
    private Object value;
}

I've tried to use this approach that I found and that is also in the documentation, but it's failing for me.

My Mapper:

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring", uses = ComponentStyleMapperUtil.class)
public interface ComponentStyleMapper {

    ComponentStyleMapper MAPPER = Mappers.getMapper(ComponentStyleMapper.class);

    @Mappings({@Mapping(target = "attribute", qualifiedBy = ComponentStyleMapperUtil.Attribute.class),
    @Mapping(target = "value", qualifiedBy = ComponentStyleMapperUtil.Value.class)})
    ComponentStyleDTO hashMapToComponentStyleDTO(HashMap<String, Object> hashMap);
}

My Util:

public class ComponentStyleMapperUtil{
    @Qualifier
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Attribute {
    }

    @Qualifier
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Value {
    }


    @Attribute
    public String attribute(HashMap<String, Object> in){

        return (String) in.entrySet().stream().findFirst().get().getKey();
    }

    @Value
    public Object value(HashMap<String, Object> in) {
        Object value = in.entrySet().stream().findFirst().get().getValue();

        if(value instanceof String){
            return value;
        }else if(value instanceof LinkedHashMap){
            List<ComponentStyleDTO> childs = new ArrayList<ComponentStyleDTO>();
            HashMap<String, Object> child = (HashMap<String, Object>) value;
            for(String key: child.keySet()){
                ComponentStyleDTO schild = new ComponentStyleDTO();
                schild.setAttribute(key);
                schild.setValue((String) child.get(key));
                childs.add(schild);
            }
            return childs;
        }else{
            return value;
        }

    }

}

And here's how I'm using this:

    HashMap<String, Object> hmap = new HashMap<String, Object>();
    hmap.put(attr.getKey(), attr.getValue());
    ComponentStyleDTO componentDTO = componentStyleMapper.hashMapToComponentStyleDTO(hmap);

But it's returning me empty in attribute and value. Any idea of what I could be doing wrong?

Cyclist answered 8/2, 2019 at 22:13 Comment(1)
Do you have getters/setters( or lombok or so) on DTO?Janinejanis
K
3

Not sure what you are trying to achieve. If your mapping is more complex maybe the best way is indeed to go with the approach in https://mcmap.net/q/1221005/-mapstruct-hashmap-as-source-to-object.

That a side, the reason why it is not working for you is that you have not defined the source for your mapping. In the example you linked there is a POJO as source parameter and the source is a map in that POJO. In order to make it work your mapper needs to look like:

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring", uses = ComponentStyleMapperUtil.class)
public interface ComponentStyleMapper {

    @Mapping(target = "attribute", source = "hashMap", qualifiedBy = ComponentStyleMapperUtil.Attribute.class)
    @Mapping(target = "value", source = "hashMap", qualifiedBy = ComponentStyleMapperUtil.Value.class)
    ComponentStyleDTO hashMapToComponentStyleDTO(HashMap<String, Object> hashMap);
}

NB: When using the non default componentModel you should not use the Mappers factory for getting an instance of the mapper. If you do that you will get an NPE when working with other mappers.

Kuibyshev answered 9/2, 2019 at 14:37 Comment(1)
Thank you. I did use another approach, but thank ou for replying a fix for this!Cyclist
E
1

IMHO The best way is the simpliest way:

default ComponentStyleDTO hashMapToComponentStyleDTO(HashMap<String, Object> hashMap){
    ComponentStyleDTO result = new ComponentStyleDTO();
    result.setAtribute1(hashMap.get("atribute1"));
    result.setAtribute2(hashMap.get("atribute2"));
    result.setAtribute3(hashMap.get("atribute3"));
    ...
    return result;
}

or

default List<ComponentStyleDTO> hashMapToComponentStyleDTO(HashMap<String, Object> hashMap){
    return hashMap.entrySet()
                  .stream()
                  .map(e -> new ComponentStyleDTO(e.getKey(), e.getValue()))
                  .collect(Collectors.toList());
}
Eating answered 8/2, 2019 at 22:22 Comment(1)
Thank you but this wouldn't work in my example since the value can be another hashmap.Cyclist
E
0

For future reference. As of Mapstruct 1.5 I think this usecase is supported out of the box. See mapstruct release note

Efficacy answered 10/11, 2021 at 19:59 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Re

© 2022 - 2024 — McMap. All rights reserved.