Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method already defined when mapping a recursive data structure into a list of the same structure #3532

Open
codeStev opened this issue Feb 16, 2024 · 1 comment
Labels

Comments

@codeStev
Copy link

Expected behavior

I've got the following data structures I would like to map:

source {

private Node node;

// getters/setters

}

target {

private List<NodeDto> nodeDtoList;

// getters/setters

}

The recursive structures "Node" and "NodeDto" look like this :

Node {

//properties

List<Node> childNodes;

// getters/setters

}

NodeDto {

//properties

List<NodeDto> childNodes;

// getters/setters

}

The map method looks as follows:


@Mapping( target = "nodeList", source = "node.childNodes" )
abstract Target map( Source source);

The reason for mapping it like this is to omit the root element of the tree since it is not wanted in this case.

MapStruct generates the methods with the following signature correctly :


NodeDto nodeToNodeDto( Node node )

List<NodeDto> nodeListToNodeDtoList( List<Node> )

I would expect MapStruct to use the already generated method to map the list and not to generate the same method twice.

Actual behavior

MapStruct generates the method List<NodeDto> nodeListToNodeDtoList( List<Node> ) twice, provoking a compile error in the generated implementation.
If I don't specify the source the root element will get mapped, which is not wanted behaviour.

The Exception Message looks like this :

error: method nodeListToNodeDtoList(List<Node>) is already defined in class TargetMapperImpl

Steps to reproduce the problem

My configuration looks as follows:

@MapperConfig(
        unmappedTargetPolicy = ReportingPolicy.ERROR,
        unmappedSourcePolicy = ReportingPolicy.IGNORE,
        componentModel = MappingConstants.ComponentModel.SPRING,
        injectionStrategy = InjectionStrategy.CONSTRUCTOR,
        nullValueIterableMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
public interface CentralMapperConfig {

}

The problem should be reproducable with the snippets described above. I omitted the getters/setters and the Mapping class to keep it more readable.

MapStruct Version

MapStruct 1.5.3

@codeStev codeStev added the bug label Feb 16, 2024
@seaworld0125
Copy link

seaworld0125 commented Feb 22, 2024

@codeStev I'll suggest this code. This code seems to do what you want.

  @Mapper(componentModel = "spring")
  interface TestMapper {

    @Mapping(source = "rootNode.childNodes", target = "childNodeDtos")
    Target map(Source source);

    @Mapping(source = "childNodes", target = "childNodeDtos")
    NodeDto map(Node node);

    record Source(Node rootNode) {

    }

    record Target(List<NodeDto> childNodeDtos) {

    }

    record Node(String name, List<Node> childNodes) {

    }

    record NodeDto(String name, List<NodeDto> childNodeDtos) {

    }
  }
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-02-22T17:52:46+0900",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 17.0.5 (Amazon.com Inc.)"
)
@Component
public class TestMapperImpl implements TestMapper {

    @Override
    public Target map(Source source) {
        if ( source == null ) {
            return null;
        }

        List<NodeDto> childNodeDtos = null;

        List<Node> childNodes = sourceRootNodeChildNodes( source );
        childNodeDtos = nodeListToNodeDtoList( childNodes );

        Target target = new Target( childNodeDtos );

        return target;
    }

    @Override
    public NodeDto map(Node node) {
        if ( node == null ) {
            return null;
        }

        List<NodeDto> childNodeDtos = null;
        String name = null;

        childNodeDtos = nodeListToNodeDtoList( node.childNodes() );
        name = node.name();

        NodeDto nodeDto = new NodeDto( name, childNodeDtos );

        return nodeDto;
    }

    private List<Node> sourceRootNodeChildNodes(Source source) {
        if ( source == null ) {
            return null;
        }
        Node rootNode = source.rootNode();
        if ( rootNode == null ) {
            return null;
        }
        List<Node> childNodes = rootNode.childNodes();
        if ( childNodes == null ) {
            return null;
        }
        return childNodes;
    }

    protected List<NodeDto> nodeListToNodeDtoList(List<Node> list) {
        if ( list == null ) {
            return null;
        }

        List<NodeDto> list1 = new ArrayList<NodeDto>( list.size() );
        for ( Node node : list ) {
            list1.add( map( node ) );
        }

        return list1;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants