hiddenfeld
hiddenfeld

Reputation: 7

How to add custom mapping to an existing mapStruct mapper?

So this colleague of mine created a simple mapper and I want to add some more features to it. I suspect mapStruct will not be able to automatically generate methods for it, so I believe I will have to write custom mapping logic. I want to map values that are nested inside the input object and saved in a key-value-pair structure to attributes of the output.

  1. Am I correct in assuming that I need to write a custom mapper?
  2. Can I attach my custom mapping logic to his existing streight forward mapper?
  3. Or do I have to replace his mapper completely by the new custom mapper?

Here is an example of the kind of structure at hand. He implemented mapping for members one, two and three, I would like to add mapping for members alpha and beta.

@Mapper()
public interface AMapper {
    @Mapping(source = "one", target = "oneX")
    @Mapping(source = "two", target = "twoX")
    @Mapping(source = "three", target = "threeX")
    OutA inAToOutA(InA inA);
}

class InA {
    String one;
    int two;
    long three;
    InB b;
}

class InB {

    List<InC> listOfC = new ArrayList<>();
    
    public InB() {
        listOfC.add(new InC("alpha", "AlphaContent"));
        listOfC.add(new InC("beta", "BetaContent"));
    }
}


class InC {
    String key;
    String value;
    
    public InC(String key, String value) {
        this.key = key;
        this.value = value;
    }
}

class OutA {
    String oneX;
    int twoX;
    long threeX;
    String alphaX;
    String betaX;
}

In my mapStruct rooky mind a solution could look something like this, but I'd rather not have to rewrite all of his stuff, cause it's bigger than this example, that's why I am asking for advice:

@Mapper()
public interface AMapper {
    default OutA getA(InA a) {
        if (a == null)
            return null;
        OutA oa = new OutA();
        oa.setOneX(a.getOne());
        oa.setTwoX(a.getTwo());
        oa.setThreeX(a.getThree());
        for (InC c : a.getB().getListOfC()) {
            switch (c.getKey()) {
                case "alpha":
                    oa.setAlphaX(c.getValue());
                    break;
                case "beta":
                    oa.setBetaX(c.getValue());
                    break;
                default:
                    break;
            }
        }
        return oa;
    }
}

Upvotes: 0

Views: 2068

Answers (1)

Georgii Lvov
Georgii Lvov

Reputation: 2701

You can use mapstruct @AfterMapping annotation in existing mapper:

@Mapper
public interface AMapper {

    @Mapping(source = "one", target = "oneX")
    @Mapping(source = "two", target = "twoX")
    @Mapping(source = "three", target = "threeX")
    @Mapping(target = "alphaX", ignore = true)
    @Mapping(target = "betaX", ignore = true)
    OutA inAToOutA(InA inA);


    @AfterMapping
    default void afterMappingInAtoOutA(@MappingTarget OutA outA, InA inA) {

        if (inA.getB() == null || inA.getB().getListOfC() == null) { // add some null cheсks
            return;
        }

        for (InC c : inA.getB().getListOfC()) {
            switch (c.getKey()) {
                case "alpha":
                    outA.setAlphaX(c.getValue());
                    break;
                case "beta":
                    outA.setBetaX(c.getValue());
                    break;
                default:
                    break;
            }
        }
    }
}

Upvotes: 1

Related Questions