mapstruct icon indicating copy to clipboard operation
mapstruct copied to clipboard

Can't use `@Mapping#source` for map keys containg the dot character

Open sajkol opened this issue 3 years ago • 5 comments

Expected behavior

Using the new map->bean mapping feature it is not possible to use @Mapping#source for keys containing a . character. Instead, the Map type itself seems to be considered as the source type and its methods are proposed as alternatives.

It's still possible to achieve that with expression of course (as in the commented out line in the example), but it's far less elegant as just using the source.

Actual behavior

error: No property named "a.b" exists in source parameter(s). Did you mean "a.bytes"?
        @Mapping(target = "value", source = "a.b")

Steps to reproduce the problem

package com.example;

import org.junit.jupiter.api.Test;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

class MapstructExampleTest {

    public static record ExampleBean(String value) { }

    @Mapper
    public interface ExampleMapper {

        @Mapping(target = "value", source = "a.b")
        //@Mapping(target = "value", expression = "java(source.get(\"a.b\"))")
        ExampleBean map(Map<String, String> source);
    }

    @Test
    void shouldMapToBean() {
        Map<String, String> source = Map.of("a.b", "test");
        var output = Mappers.getMapper(ExampleMapper.class).map(source);
        assertThat(output.value()).isEqualTo("test");
    }

}

MapStruct Version

1.5.3

sajkol avatar Nov 02 '22 17:11 sajkol

Hi, I guess you can take a look at this document

chenzijia12300 avatar Nov 03 '22 14:11 chenzijia12300

Thank you, I have of course read through the documentation and described behavior seems to contradict what's written there:

using the target bean properties (or defined through Mapping#source) to extract the values from the map

The example even shows usage of Mapping#source to directly name the field in the source map from which a value should be taken. There's no mention here of the . character being treated in a special way as it is for bean mappings - but in that case, it's used to support nested beans and the characters can't be used in a field name anyway. For String map keys it's no longer a special character and unless there is a plan to have support for multi-level maps as mapping sources, there's no point in having any special handling for it. Furthermore, the error message suggests the Map type itself is treated as source type (as opposed to map contents), which also supports the idea that this is not intended behavior.

sajkol avatar Nov 04 '22 09:11 sajkol

I think you are right, we may need to think about how to handle the dot character in map in a special way

chenzijia12300 avatar Nov 05 '22 07:11 chenzijia12300

Hi, are there any updates on this?

azizairo avatar Dec 08 '23 08:12 azizairo

Hi, are there any updates on this?

As sajkol wrote above, you can use the expression attribute of the Mapping annotation to do a bit of it e.g.

@Mapper
public interface ExampleMapper {

    @Mapping(target = "value", expression = "java(source.get( \"a.b\" ))")
    ExampleBean map(Map<String, String> source);

}

will generate

public class ExampleMapperImpl implements ExampleMapper {

    @Override
    public ExampleBean map(Map<String, String> source) {
        if ( source == null ) {
            return null;
        }

        ExampleBean exampleBean = new ExampleBean();

        exampleBean.setValue( source.get( "a.b" ) );

        return exampleBean;
    }
}

chenzijia12300 avatar Dec 10 '23 06:12 chenzijia12300