Search code examples
jaxbwsdlmapstruct

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


I was going to create an issue on github, but the issue template says I'd rather discuss it here first, so here we go:

I am trying to use Mapstruct to generate mappings from one WSDL-generated set of entities to another. However, without adding a "default" method (manual mapping) it does not work ! That seems to be strange, as I would expect this kind of mapping should not be difficult for Mapstruct. repro case is here: https://github.com/62mkv/wsdl-mapstruct-repro-case

the gist of the code is here:

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

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

}

this code above will fail to compile, with such message:

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

basically, mapping would go as follows: EntityNew -> JaxbElement -> FieldNew -> FieldOld -> JaxbElement -> EntityOld

as I've read here (https://stackoverflow.com/a/46015381/2583044), mapping from JaxbElement to T is trivial for MapStruct, and to map from T to JaxbElement one has to use "uses" annotation and provide ObjectFactory.class, which I do; however... this seems to not be enough.

if I add these two methods, code compiles good:

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

is it a potential issue in mapstruct or I just don't know how to cook it well?


Solution

  • The problem is that MapStruct sees your object factory method (with argument) as a mapping method. So, it delivers a target, but it has a source as well. If you realise this, then the mapping suddenly is not symmetrical (as it initially appears).

    The simple solution is to instruct MapStruct how to handle this.

    So: try this:

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

    Last but not least, you need to define the first method garlicParameterCollectionToGarlicParameterCollection which surprised me initially.

    The reason: MapStruct either tries to: 1. find a mapping method (which is not there if you leave this one out) or 2. tries to generate a direct mapping (by inspecting if it can find methods for all the attributes on source and target).

    However, MapStruct cannot find a direct case for this mapping (it would in principle needs to apply all other possible mappings on its path (e.g. all the methods in the object factory) and then try to generate a mapping method as explained in 2, which could be a lot of combinations. That functionality is not there (and would be load intensive as well I guess).