Home > database >  MapStruct nested list source to flatten target
MapStruct nested list source to flatten target

Time:10-26

How can I map the following:

class Source {
   String name;
   List<Other> others;
}
class Other {
   String otherName;
   List<More> mores;
}
class More {
   String moreName;
}

class Target {
   String name;
   String otherName;
   String moreName;
}

Hoping it could be something like:

@Mapper
public interface MyMapper {

    @Mapping(target = "name", source = "name")
    @Mapping(target = "otherName", source = "others[0].otherName")
    @Mapping(target = "morename", source = "others[0].mores[0].moreName")
    Target map(Source source);

I see that 'expression = "java(others.get(0).otherName)"' can be used but there are no null checks when using expression, which could result in NullPointeException. Any suggestions with null checks and default can be applied?

CodePudding user response:

I think you need to see this example: MapStruct String to List mapping

Basically, you need to verify the size of the list before getting some elements.

CodePudding user response:

You can use expression to check the null value and return something.

@Mapper
public interface MyMapper {
    @Mapping(target = "name", source = "name")
    @Mapping(target = "otherName", expression = "java(s.others != null && !s.others.isEmpty() ? s.others.get(0).otherName : \"\")")
    @Mapping(target = "moreName", expression = "java(s.others != null && !s.others.isEmpty() && s.others.get(0).mores != null && !s.others.get(0).mores.isEmpty() ? s.others.get(0).mores.get(0).moreName : \"\")")
    Target map(Source s);
}

// mapstruct code generate.
public class MyMapperImpl implements MyMapper {
    @Override
    public Target map(Source s) {
        if ( s == null ) {
            return null;
        }

        Target target = new Target();

        target.name = s.name;

        target.otherName = s.others != null && !s.others.isEmpty() ? s.others.get(0).otherName : "";
        target.moreName = s.others != null && !s.others.isEmpty() && s.others.get(0).mores != null && !s.others.get(0).mores.isEmpty() ? s.others.get(0).mores.get(0).moreName : "";

        return target;
    }
}

If you want readable and reduce the check null duplicate code, you can use the interface default method and use org.springframework.util.ObjectUtils.isEmpty().

@Mapper
public interface MyMapper {
    @Mapping(target = "name", source = "name")
    @Mapping(target = "otherName", expression = "java(getFirstOtherName(s))")
    @Mapping(target = "moreName", expression = "java(getFirstMoreName(s))")
    Target map(Source s);

    default String getFirstOtherName(Source s) {
        return !ObjectUtils.isEmpty(s.others) ? s.others.get(0).otherName : "";
    }

    default String getFirstMoreName(Source s) {
        return !ObjectUtils.isEmpty(s.others) && !ObjectUtils.isEmpty(s.others.get(0).mores) ? s.others.get(0).mores.get(0).moreName : "";
    }
}

// mapstruct code generate.
public class MyMapperImpl implements MyMapper {

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

        Target target = new Target();

        target.name = s.name;

        target.otherName = getFirstOtherName(s);
        target.moreName = getFirstMoreName(s);

        return target;
    }
} 
  • Related