如何使用 MapStruct 从 JaxbElement<X> 映射到 JaxbElement<Y>

how to map from JaxbElement<X> to JaxbElement<Y> with MapStruct

我本来打算在 github 上创建一个问题,但问题模板说我宁愿先在这里讨论它,所以我们开始吧:

我正在尝试使用 Mapstruct 生成从一组 WSDL 生成的实体到另一组实体的映射。但是,如果不添加 "default" 方法(手动映射),它是行不通的!这似乎很奇怪,因为我认为这种映射对于 Mapstruct 应该不难。重现案例在这里:https://github.com/62mkv/wsdl-mapstruct-repro-case

代码的要点在这里:

@Mapper(uses = {
    org.system.wsdl.legacy.ObjectFactory.class
})
public interface WsMapper {

org.system.wsdl.legacy.SellGarlicRequest fromCloud(org.system.wsdl.cloud.SellGarlicRequest request);

}

以上代码将无法编译,并显示以下消息:

Can't map property "javax.xml.bind.JAXBElement inputParameters" to "javax.xml.bind.JAXBElement inputParameters". Consider to declare/implement a mapping method: "javax.xml.bind.JAXBElement map(javax.xml.bind.JAXBElement value)". org.system.wsdl.legacy.SellGarlicRequest fromCloud(org.system.wsdl.cloud.SellGarlicRequest request);

基本上,映射如下:EntityNew -> JaxbElement -> FieldNew -> FieldOld -> JaxbElement -> EntityOld

正如我在这里读到的那样 (),从 JaxbElement 到 T 的映射对于 MapStruct 来说是微不足道的,要从 T 映射到 JaxbElement,必须使用 "uses" 注释并提供 ObjectFactory.class,我做的;然而……这似乎还不够。

如果我添加这两个方法,代码编译良好:

org.system.wsdl.legacy.GarlicParameterCollection garlicParameterCollectionToGarlicParameterCollection(org.system.wsdl.cloud.GarlicParameterCollection collection);

default JAXBElement<org.system.wsdl.legacy.GarlicParameterCollection> garlicParameterCollectionToGarlicParameterCollection(JAXBElement<org.system.wsdl.cloud.GarlicParameterCollection> parameterCollectionJAXBElement) {
    return new org.system.wsdl.legacy.ObjectFactory().createSellGarlicRequestInputParameters(
            this.garlicParameterCollectionToGarlicParameterCollection(parameterCollectionJAXBElement.getValue())
    );
}

它是 mapstruct 中的一个潜在问题还是我只是不知道如何煮好?

问题是 MapStruct 将您的对象工厂方法(带参数)视为映射方法。因此,它提供了一个目标,但它也有一个来源。如果你意识到这一点,那么映射就会突然不对称(正如它最初出现的那样)。

简单的解决方案是指示 MapStruct 如何处理此问题。

所以:试试这个:


@Mapper(uses = {
        org.system.wsdl.legacy.ObjectFactory.class
})
public interface WsMapper {

    org.system.wsdl.legacy.GarlicParameterCollection garlicParameterCollectionToGarlicParameterCollection(org.system.wsdl.cloud.GarlicParameterCollection collection);

    @Mapping( target = "inputParameters", source = "inputParameters.value") // this instructs MapStruct to use value of the source JAXBElement (for which it has an object factory method) instead of trying to map JAXBElement to JAXBElement.
    org.system.wsdl.legacy.SellGarlicRequest fromCloud(org.system.wsdl.cloud.SellGarlicRequest request);

}

最后但同样重要的是,您需要定义第一个方法 garlicParameterCollectionToGarlicParameterCollection 最初让我感到惊讶。

原因:MapStruct 要么尝试: 1.找到一个映射方法(如果你把这个去掉就没有了) 要么 2. 尝试生成直接映射(通过检查它是否可以找到源和目标上所有属性的方法)。

但是,MapStruct 无法找到此映射的直接案例(原则上它需要在其路径上应用所有其他可能的映射(例如对象工厂中的所有方法),然后尝试生成一个映射方法作为在 2 中解释,可能有很多组合。该功能不存在(我猜也会是负载密集型的)。