javajaxbmapstructobject-object-mapping

How to map JAXB elements annotated with @XMLSeeAlso using mapStruct?


I'm trying to map a bean which has some JAXB elements like @XmlSeeAlso, @XmlElement, @XmlSchemaType as properties for that class.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Customer")
@XmlSeeAlso({PersonalCustomer.class, BusinessCustomer.class})
public class Customer extends Role {

 @XmlElement(name = "AMLLineOfBusiness")
private LOB amlLineOfBusiness;
 // 50 odd properties
 //some properties with XmlElement/XmlSchemaType 
 // getters and setters   
}

@Mapper
public interface CustomerMapper {
     PersonalCustomer personcalCustomerToPersonalCustomer(PersonalCustomer pc);
@Mappings({
    /*Several other ignore mappings*/
    @Mapping(target="AMLLineOfBusiness",ignore=true)
    })
     Customer customerToCustomer(Customer customer);
   }

Now I'm facing below issues when mapping using mapStruct.

  1. The bean PersonalCustomer is not being mapped. I don't see it in the response. But it works perfectly when using dozer. All I need to do is define it in it's mapping in dozer config xml. I tried similar thing in mapStruct. I defined personcalCustomerToPersonalCustomermethod in mapper interface with required mappings and ignoring not required fields. Although implementation is there in the mapperImpl but I don't see it being used anywhere in the impl class.

  2. While ignoring the fields which have either @XmlElement or @XmlSchemaType, I'm getting compilation error while generating impl code. Below is the error statck trace. I got compilation error when I used amlLineOfBusiness.

    [70,2] error: Unknown property "amlLineOfBusiness" in result type com.role.Customer. Did you mean "lineOfBusiness"? [ERROR] -> [Help 1] org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.5.1:compile (default-compile) on project service-jar: Compilation failure C:\Perforce\service-jar\2018.08.0\service-jar\src\main\java\com\mapstruct\mapper\CustomerMapper.java:[70,2] error: Unknown property "amlLineOfBusiness" in result type com.role.Customer. Did you mean "lineOfBusiness"? at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:108) at org.apache.maven.lifecycle.internal.builder.multithreaded.MultiThreadedBuilder$1.call(MultiThreadedBuilder.java:188) at org.apache.maven.lifecycle.internal.builder.multithreaded.MultiThreadedBuilder$1.call(MultiThreadedBuilder.java:184) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: org.apache.maven.plugin.compiler.CompilationFailureException: Compilation failure

    The above is resolved when I used the name value of (@XmlElement(name="AMLLineOfBusiness")) annotation to ignore. I couldn't understand what exactly going on here.

  3. By default mapStruct is mapping fields of super class too. In my case, Customer class also getting properties of Role. There is no other option except using ignore=true on each property. This is kind of hectic as I'm having 50 odd fields and tens of similar mappers based on service requirement. I wish mapStruct had feature to ignore all fields by default or a strategy to map only specified fields.

Note that both source and destination types are same here. I only need to map certain fields depending on the requirement. I'm stuck here. I highly appreciate your help.


Solution

  • It seems like you have 3 different problems.

    1. It seems what you are trying to achieve is for MapStruct to detect all possible implementations for Customer (or see @XmlSeeAlso) and use the method you need. This is not possible automatically in MapStruct. See #131 for an existing feature request.
    2. This should happen when you have not defined the property correct. MapStruct actually looks into the getters and setters only (not in the field). So if you getter is getAM then your @Mapping(target = "AMLLineOfBusiness", ignore = true)
    3. This is similar with this question. Maybe you can try Reusing mapping configurations

    A possible solution for 1 would be you to an instance of on your side.

    @Mapper
    public interface CustomerMapper {
        PersonalCustomer personcalCustomerToPersonalCustomer(PersonalCustomer pc);
    
        default Customer customerToCustomer(Customer customer) {
            if (customer instanceOf PersonalCustomer) {
                return personalCustomerToPersonalCustomer((PersonalCustomer) pc);
            } else if (customer instanceOf BusinessCustomer) {
                return businessCustomerToBusinessCustomer((BusinessCustomer) pc);
            }
        }
    }
    

    The reason for such things is that MapStruct is an annotation processor so it generated code during compilation time. On the other side Dozer is working with runtime information. Dozer can get the class during runtime and pick the right method. MapStruct cannot deduce all the possible implementations.