Search code examples
javaxmljaxbgeneric-programming

How to pass parameter with generic: Class<Clazz<G>>


I try to parse a XML(String) to a Object using JAXB, but I can't find the way to call the method I had created because I don't know how to specify the class BasicRequest<G> with the SomeRequest as G

public class XMLParser {

    public <G extends BaseRequest> BasicRequest<G> xmlToRequest(String xml, 
            Class<BasicRequest<G>> t) {
        BasicRequest<G> request = null;
        try {
            JAXBContext context = JAXBContext.instance(t);
            Unmarshaller m = context.createUnmarshaller();
            StringReader reader = new StringReader(xml);
            JAXBElement<BasicRequest<G>> unmarshal = m.unmarshal(new 
                StreamSource(reader), t);
            request = unmarshal.getValue();
        } catch (JAXBException ex) {
            //write log
        }
        return request;
    }
}

This are my classes:

public abstract class BaseRequest {
    protected String version;

    //getter & setter
}

,

public class SomeRequest extends BaseRequest {
    protected Integer id;

    //getter & setter
}

and

public class BasicRequest<G extends BaseRequest> {
    protected String user;
    protected G data;

    //getter & setter
}

All classes actually have the JAXB annotation correctly.

If someone could please give me a hand on how should I call the method or a better way to define it I would appreciate.


Solution

  • Perhaps someone better-versed in JAXB has a clever JAXB-centric solution, but from the standpoint of generics, I do not believe there is a way to do what you are trying to do. The type parameter information for the enclosing BasicRequest class is erased at runtime (see Class object of generic class (java)), so any Class object representing a BasicRequest will simply reflect the raw type. (Correspondingly, the Unmarshaller will have no way to know what that parameter is intended to be.)

    You could try one of the following solutions:

    1. If you are certain that what the Unmarshaller returns will conform to BasicRequest, then you could cast the result to JAXBElement<BasicRequest<G>>. The cast will produce an unchecked cast warning (again, because of type erasure), which can be suppressed with the @SuppressWarnings("unchecked") annotation on the enclosing method, if you wish.
    2. For each class you have which could take the place of G, you could create a subclass which extends BasicRequest parameterized with that class, i.e.:

      public class SomeBasicRequest extends BasicRequest<SomeRequest> {
      ...
      }
      

      SomeBasicRequest would not be assignable from Class<BasicRequest<G>>, though, so you'd have to change the method header as follows:

      public <G extends BaseRequest> BasicRequest<G> xmlToRequest(String xml, Class<? extends BasicRequest<G>> t) {
      ...
      }