env:
Using the default configuration I generated the following code
protected AgentInfo wealthProdAccountInfoDTOToAgentInfo(WealthProdAccountInfoDTO wealthProdAccountInfoDTO) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
String agentName = null;
String agentIdentityType = null;
String agentIdentityNo = null;
String agentIdentityExpireAt = null;
agentName = wealthProdAccountInfoDTO.getAgentName();
agentIdentityType = wealthProdAccountInfoDTO.getAgentIdentityType();
agentIdentityNo = wealthProdAccountInfoDTO.getAgentIdentityNo();
agentIdentityExpireAt = wealthProdAccountInfoDTO.getAgentIdentityExpireAt();
AgentInfo agentInfo = new AgentInfo( agentName, agentIdentityType, agentIdentityNo, agentIdentityExpireAt );
return agentInfo;
}
But I want to return null when all field of source are null, like this
protected AgentInfo wealthProdAccountInfoDTOToAgentInfo(WealthProdAccountInfoDTO wealthProdAccountInfoDTO) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
// add check logic
if (agentName == null && agentIdentityType == null && agentIdentityNo == null && agentIdentityExpireAt == null) {
return null;
}
String agentName = null;
String agentIdentityType = null;
String agentIdentityNo = null;
String agentIdentityExpireAt = null;
agentName = wealthProdAccountInfoDTO.getAgentName();
agentIdentityType = wealthProdAccountInfoDTO.getAgentIdentityType();
agentIdentityNo = wealthProdAccountInfoDTO.getAgentIdentityNo();
agentIdentityExpireAt = wealthProdAccountInfoDTO.getAgentIdentityExpireAt();
AgentInfo agentInfo = new AgentInfo( agentName, agentIdentityType, agentIdentityNo, agentIdentityExpireAt );
return agentInfo;
}
how should I configure it?
Unfortunately there's no clean solution for your problem, except implementing code for null check by yourself, Marc specified the right approach to your problem (I'd go with it personally or would use default method for the same purpose).
I can add some workarounds, which will only work if mapping target is inner object:
Use @BeforeMapping
to set input inner object to null, so when there will be null-check it will be skipped
@BeforeMapping default void clearData(TestB source, @MappingTarget TestA target) { TestD innerD = source.getInnerD(); if (innerD.getSecond() == null && innerD.getFirst() == null) { source.setInnerD(null); } }
And it will generate the following code:
@Override public TestA from(TestB input) { .... clearData( input, testA ); //set input field to null testA.setInnerC( fromInner( input.getInnerD() ) ); .... } @Override public TestC fromInner(TestD input) { if ( input == null ) { //skip because of null return null; } .... }
Use @AfterMapper
to set output parameter to null(it will be mapped in the first place, so there will be some overhead)
@AfterMapping default void clearData(TestB source, @MappingTarget TestA target) { TestD innerD = source.getInnerD(); if (innerD.getSecond() == null && innerD.getFirst() == null) { target.setInnerC(null); } }
And generated code will be:
@Override public TestA from(TestB input) { .... testA.setInnerC( fromInner( input.getInnerD() ) ); //field is actually mapped but cleared later clearData( input, testA ); return testA; }
As I said, these solutions aren't really clean and should be seen as workarounds only. Pros of these workaround is that you will keep working with autogenerated code and these hacks will be hidden inside that code.
UPD Stumbled upon @DecoratedWith
lately and it also can do the trick. https://mapstruct.org/documentation/stable/reference/html/#_customizing_mappings
Just implement decorator for iterable2iterable mapping method: List<A> from(List<b> b)
and just manually iterate over b checking if all b's fields are null and if so skip it
brute force... it's a simple class, so create a custom mapper
@Mapper
public interface AgentInfoMapper {
@Named("AgentInfoNullIfContentsNull")
public static AgentInfo custom(WealthProdAccountInfoDTO dto) {
if ( wealthProdAccountInfoDTO == null ) {
return null;
}
if (agentName == null && agentIdentityType == null && agentIdentityNo == null && agentIdentityExpireAt == null) {
return null;
}
// mapping code
}
}
Thanks to ArtemAgaev's idea, I ended up considering using @AfterMapping
and java reflection for this type of scenario
@AfterMapping
default void cleanData(@MappingTarget AccountInfoDomain domain) {
Optional.ofNullable(domain).ifPresent(c -> {
if (isAllFieldNull(domain.getAgentInfo())) {
domain.setAgentInfo(null);
}
});
}
public static boolean isAllFieldNull(Object o) {
Object[] fieldsValue = getFieldsValue(o);
return Optional.ofNullable(fieldsValue).map(f -> Arrays.stream(f).allMatch(Objects::isNull)).orElse(true);
}
public static Object[] getFieldsValue(Object obj) {
if (null != obj) {
final Field[] fields = getFields(obj instanceof Class ? (Class<?>) obj : obj.getClass());
if (null != fields) {
final Object[] values = new Object[fields.length];
for (int i = 0; i < fields.length; i++) {
values[i] = getFieldValue(obj, fields[i]);
}
return values;
}
}
return null;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.