Joseph K. Strauss
Joseph K. Strauss

Reputation: 4913

MapStruct problems with nested properties in combination with multiple parameters

I am trying to map from one nested object to another with some custom mappings, and I also need to add an additional field that is not present in the source object by using a second method parameter. I am getting either a compiler error when the target type uses constructor mapping or incomplete mapping when using mutable objects.

Is this a bug? Or am I missing something?

Some non-optimal workarounds:

My Model

record FromInner1(String a, String b, String c) {}
record FromInner2(String d, String e) {}
record FromOuter(FromInner1 inner1, FromInner2 inner2) {}

record ToInner1(String a, String b, String c) {}
record ToInner2(String d, String e, String additional) {}
record ToOuter(ToInner1 inner1, ToInner2 inner2) {}

My Mapper

@Mapping(target = "inner2.d", source = "fromOuter.inner2.d", qualifiedByName = "transform")
//@Mapping(target = "inner2.e", source = "fromOuter.inner2.e")
@Mapping(target = "inner2.additional", source = "additionalField")
ToOuter toOuter(FromOuter fromOuter, String additionalField);

@Named("transform")
default String transform(String original) {
  return original.trim().toUpperCase();
}

Compiler Errors

java: Property "d" has no write accessor in ToInner2 for target name "inner2.d".
java: Property "additional" has no write accessor in ToInner2 for target name "inner2.additional". 

Mutable Model

Using a mutable model, i.e., using the same fields, but making a no-args constructor, all-args constructor getters and setters for all the fields results in the following result test result

@Test
void toOuter() {
  assertThat(
        mapper.toOuter(
            new FromOuter(new FromInner1("a", "b", "c"), new FromInner2("    d    ", "e")),
            "additional"))
    .isEqualTo(new ToOuter(new ToInner1("a", "b", "c"), new ToInner2("D", "e", "additional")));
}

Expected :ToOuter(inner1=ToInner1(a=a, b=b, c=c), inner2=ToInner2(d=D, e=e, additional=additional))
Actual   :ToOuter(inner1=ToInner1(a=a, b=b, c=c), inner2=ToInner2(d=D, e=null, additional=additional))

Workaround

@Mapping(target = "inner2", expression = "java(toInner2(fromOuter.inner2(), additionalField))") //compiles, but does not change anything
ToOuter toOuter(FromOuter fromOuter, String additionalField);

@Named("transform")
default String transform(String original) {
  return original.trim().toUpperCase();
}

@Mapping(target = "d", source = "fromInner2.d", qualifiedByName = "transform")
@Mapping(target = "additional", source = "additionalField")
ToInner2 toInner2(FromInner2 fromInner2, String additionalField);

Upvotes: 0

Views: 29

Answers (0)

Related Questions