mapstruct / mapstruct.org

Web site of the MapStruct project
63 stars 40 forks source link

MapStruct not mapping JAXB beans with @XMLSeeAlso annotation #75

Closed Naveen-Kumar-Reddy-Murthi closed 6 years ago

Naveen-Kumar-Reddy-Murthi commented 6 years ago

I have the following Java class structure and it's respective mapstruct mapping. Here source and destination beans in mapping are same. I only need to map specific fields to send to a client based on their requirement.

@XmlSeeAlso({A.class, B.class})
public class C extends D implements Serializable {
  //50 odd properties
 // getters&Setters
}
public class A extends C{
 //properties
//getters&setters
}
public class B  extends C{
//properties
//getters&Setters
}
public class D{
//properties
//getters&Setters
}
@Mapper
public interface CMapper{
 C cToC(C c);
A aToA(A a);
B bToB(B b);
D dToD(D d);
//and remaining requrired mappers here
}
public class CMapperImpl implements CMapper{
public C cToC(C c){
}
//mapping logic

A aToA(A a){
//mapping logic
}

B bToB(B b){
//mapping logic
}

D dToD(D d){
//mapping logic
}

}

Code is being generated for all the defined methods in mapper interface. But methods which are defined for mapping A,B classes into C are not being used/called anywhere in the generated code. So fields of those two classes are not being mapped to resultant bean. This works fine when i use dozer. I had to simply specify bean mapping for both A&B with required fields in config xml file. However, It's not working with mapStruct.
@gunnarmorling @sradi @chschu

filiphr commented 6 years ago

@mnreddy7 This is the wrong place to open such an issues. You should be using the https://github.com/mapstruct/mapstruct/issues instead. In case such feature request (although not for JAXB @XmlSeeAlso). See https://github.com/mapstruct/mapstruct/issues/131.

Dozer works, because dozer detects the type during runtime, MapStruct is an annotation processor that runs during compilation type.

Naveen-Kumar-Reddy-Murthi commented 6 years ago

Apologies. So mapStruct doesn't support @XmlSeeAlso ? If that's the case, mapStruct can't be used for any middle ware applications.

filiphr commented 6 years ago

It has nothing to do with @XmlSeeAlso although MapStruct could possible use that information for the issues I linked.

And what do you mean by it can't be used in middleware applications? There is a way to achieve what you are looking for. Have a look at my reply on your SO question. You can also join our gitter chat for question and discussion

Naveen-Kumar-Reddy-Murthi commented 6 years ago

I've restriction to gitter from my work place. Try to join there from home and explain you in detail. Should I raise this as an issue here https://github.com/mapstruct/mapstruct/issues ?

filiphr commented 6 years ago

Well there is already an open issue for that. You can follow that one

Naveen-Kumar-Reddy-Murthi commented 6 years ago

@filiphr Thanks a lot. Actually, I'm doing a poc for our project to improve performance as current dozer mapping is slow due to it's usage of reflection. Apart from above issue, I couldn't find any issue with mapStruct. It would be great if I can find a workaround for the stated issue. I've explained the few issues with mapStruct here.

filiphr commented 6 years ago

@mnreddy7 I am the guy that wrote the answer 😀. Best would be to hoin the chat and we can discuss there in detail.

Naveen-Kumar-Reddy-Murthi commented 6 years ago

Oh. You are. I didn't observe your user names. Thanks a lot mate.

sjaakd commented 6 years ago

@mnreddy7 : the way I understand @XmlSeeAlso is used to load the proper classes in the JAXBContext. In a XML JAXB Object structure it can be that you want a certain class to be unmarshalled. That class might want to load a JAXBElement<? extends BaseClass>. By means of the XmlSeeAlso annotation, the JAXBContext knows which alternatives are available, and which types to load as candidate for unmarshalling.

You (as developer) at the other hand, know which class should be there (as derived class of BaseClass) in a specific scenario. To enable that there's a the following parameter: @BeanMapping#resultType to control the response type.

There is support however in MapStruct to determine which specific class to use in a mapping by means of its context. That might be handy if your XSD refers to a the same type at several places in your xml object structure. See the documentation for more info.

Naveen-Kumar-Reddy-Murthi commented 6 years ago

@sjaakd Documentation for @BeanMapping isn't that clear. Can you add a clear example to mapStruct examples on github repo?

Naveen-Kumar-Reddy-Murthi commented 6 years ago

@sjaakd Do you know when the next release for mapstruct is scheduled? I need this feature badly. I hope it will be available in coming release.

filiphr commented 6 years ago

@mnreddy7 what's not clear about it? There are different use cases about it. Basically it is about controlling the way a mapping is performed. Look at it's properties to understand it better.

As for the feature you linked, it would be part of the next release. We are not sure exactly when that would happen though. Although I am confident it would be soonish 😉

Naveen-Kumar-Reddy-Murthi commented 6 years ago

This is an extension to this question.

class Customer{
  // distinct properties
}
class RetailCustomer extends Customer{
  // distinct properties
}

class WholeSaleCustomer extends Customer{
 // distinct properties
}

class CustomerDO {
 // String custType ; // flag used to determine if Customer is wholeSale or Retail
 //few properties same as Customer/WholeSaleCustomer/RetailCustomer
 // few distinct properties
}

@Mapper
public interface CustomerMapper{

           default Customer toCustomer(CustomerDO customerDO) {
                 String custType = customerDO.getCustType();
                if("W".equalsIgnoreCase(custType)){
                   return toWholeSaleCustomer(customerDO);
                  }
                 else {
                  return toRetailCustomer(CustomerDO);
                   }
           }
        @Mappings({
              @Mapping(source="a", target="b"),
               @Mapping(source="c", target="d"),
               @Mapping(source="m", target="m")
            })
         WholeSaleCustomer toWholeSaleCustomer(CustomerDO customerDO);

       @Mappings({
              @Mapping(source="e", target="f"),
               @Mapping(source="g", target="h"),
               @Mapping(source="n", target="n")
            })
         RetailCustomer toRetailCustomer(CustomerDO customerDO);
}

I need a one way mapping. I need to map from CustomerDO to WholeSaleCustomer/RetailCustomer based on custType flag in CustomerDO. But this doesn't work. It gives me Ambiguous mapping methods found for mapping property exception. Is there any workaround for this. How can I use BeanMapping annotation here as suggested by @sjaakd

filiphr commented 6 years ago

@mnreddy7 please use Stackoverview or our forum for asking new questions. The issue tracker is for bugs and feature requests.

In any case you don't need that @BeanMapping. You need Mapping#qualifiedByName or Mapping#qualifiedBy

Let's assume that a in WholeSaleCustomer is of type Customer. You would need to provide:

@Mappings({
              @Mapping(source="a", target="b", qualifiedByName = "baseCustomer")
            })
         WholeSaleCustomer toWholeSaleCustomer(CustomerDO customerDO);

And you need to annotated your toCustomer method with @Named("baseCustomer"). Named should be org.mapstruct.Named.

If this does not answer your question. Please post on Stackoverflow or our forum and include the errors that you are getting.

Naveen-Kumar-Reddy-Murthi commented 6 years ago

@filiphr Sorry. Stopping here. I also post it as an issue here. forum is blocked in our network. I needed more attention to this question, so posted here. Will use stackoverflow from here on