当前位置: 首页 > 知识库问答 >
问题:

如何配置mapstruct以在所有字段都为空时忽略映射对象

赏成益
2023-03-14

环境:

  • jdk:17.0.1
  • 映射:1.5.1.Final

使用默认配置,我生成了以下代码

        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;
    }

但我想在source的所有字段都为null时返回null,如下所示

    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;
    }

我应该如何配置它?

共有3个答案

姬国安
2023-03-14

多亏了ArtemAgaev的想法,我最终考虑在这种场景中使用@AfterMapping和java反射

java prettyprint-override">    @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;
    }

王伯寅
2023-03-14

蛮力…这是一个简单的类,所以创建一个自定义映射器

@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
    }
}

https://www.baeldung.com/mapstruct-custom-mapper

段干弘扬
2023-03-14

不幸的是,你的问题没有干净的解决方案,除了自己实现空检查的代码,Marc指定了正确的方法来解决你的问题(我个人会同意,或者出于同样的目的使用默认方法)。

我可以添加一些变通方法,仅当映射目标是内部对象时才有效:

>

  • 使用@BeforeMap将输入内部对象设置为null,因此当出现null-check时将跳过它

        @BeforeMapping
    default void clearData(TestB source, @MappingTarget TestA target) {
        TestD innerD = source.getInnerD();
        if (innerD.getSecond() == null && innerD.getFirst() == null) {
            source.setInnerD(null);
        }
    }
    

    它将生成以下代码:

        @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;
        }
        ....
    }
    

    使用@AfterMapper将输出参数设置为null(它将首先被映射,因此会有一些开销)

    @AfterMapping
    default void clearData(TestB source, @MappingTarget TestA target) {
        TestD innerD = source.getInnerD();
        if (innerD.getSecond() == null && innerD.getFirst() == null) {
            target.setInnerC(null);
        }
    }
    

    并且生成的代码将是:

        @Override
    public TestA from(TestB input) {
        ....
    
        testA.setInnerC( fromInner( input.getInnerD() ) ); //field is actually mapped but cleared later
        clearData( input, testA );
    
        return testA;
    }
    

    正如我所说,这些解决方案并不是真正干净的,应该只被视为变通方法。这些变通方法的优点是您将继续使用自动生成的代码,并且这些黑客将隐藏在该代码中。

    UPD最近偶然发现了< code>@DecoratedWith,它也可以做到这一点。https://map struct . org/documentation/stable/reference/html/# _ customizing _ mappings

    只需为iterable2iterable映射方法实现装饰器:List

  •  类似资料:
    • 我使用以下映射器映射实体: 对于映射为集合的实体,我只需要忽略“数据”字段。但看起来仅适用于单个实体。我还注意到生成的方法只是在for循环中使用。对此有什么解决方案吗?

    • 我正在使用Jackson,我有一些JSON模式对象设置如下: 我试图忽略所有为空或空的字段,这工作正常。但我也想忽略字段全部为空或空的对象。例如: 生成的 JSON 字符串是 {“name”:“John Doe”,“child”:{},“sibling”:{}},但我希望它是 。创建 时需要初始化和,所以我不想更改它。有没有办法让杰克逊使用自定义序列化程序将具有空字段的对象视为空?我已经看到了对特

    • 大家好! 我正在Spring应用程序中实现Mapstruct映射器。 有两个相关实体:和。只是为了简化->用户可以有许多订单。 OrderMapper类: 好吧,我的问题是: 如果我使用以下命令传递OrderDTO: 前面描述的情况在我的应用程序中造成了巨大的问题。如果有任何帮助,我将不胜感激。

    • 我需要将多个dto映射到实体。几乎所有的dto都有一个指向另一个dto的引用,我不知道在映射dto时如何传递引用。 以下是该地址的一个业务实现: 这是地图绘制者 所有实现者都从同一个dto读取,然后将它们映射到dto和entites。 以下是针对客户实施的建议: 以及我希望引用映射地址实体的映射(我的客户实体有一个地址类型字段)。 非常感谢你。

    • 有几种方法可以忽略mapstruct中未映射的目标属性。 对于特定方法,我们可以列出要忽略的所有属性: 是否有一种方法可以混合这些方法并忽略方法级别的所有属性,而无需明确列出所有属性?

    • 我有一个Json数组,数组中包含< code>null值。 如何配置Jackson ObjectMapper以忽略此类数组元素-就好像它是空数组? 约束: 没有控制源类-它是第三方类 数组元素类型前期未知 数组名称前面未知