Unmarshalling liste générique avec JAXB

J'ai un service qui retourne ce XML:

<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<result>
    <project>
        <id>id1</id>
            <owner>owner1</owner>
    </project>
    <project>
        <id>id2</id>
            <owner>owner2</owner>
    </project>
</result>

ou

<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<result>
    <user>
        <id>id1</id>
        <name>name1</name>
    </user>
    <user>
        <id>id2</id>
            <name>name2</name>
    </user>
</result>

Je veux unmarshall le XML récupéré à l'aide de ces classes:

Résultat:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {

  @XmlElement
  protected String status;

  @XmlElementWrapper(name = "result")
  @XmlElement
  protected List<T> result;
}

Projet:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Project {

  @XmlElement
  public String id;

  @XmlElement
  public String owner;
}

Utilisateur:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class User {

  @XmlElement
  public String id;

  @XmlElement
  public String name;
}

Premier pas de solution de travail

JAXBContext context = JAXBContext.newInstance(Response.class, Project.class, User.class);
Unmarshaller unmarshaller = context.createUnmarshaller();

StreamSource source = new StreamSource(new File("responseProject.xml"));
Response<Project> responseProject = (Response<Project>)unmarshaller.unmarshal(source);
System.out.println(responseProject.getStatus());
for (Project project:responseProject.getResult()) System.out.println(project);

source = new StreamSource(new File("responseUser.xml"));
Response<User> responseUser = (Response<User>)unmarshaller.unmarshal(source);
System.out.println(responseUser.getStatus());
for (User user:responseUser.getResult()) System.out.println(user);

- Je obtenir une liste vide.

Deuxième ne fonctionne pas solution

Inspiré par cet article http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html j'ai modifié la classe de Réponse:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {

  @XmlElement
  protected String status;

  @XmlAnyElement(lax=true)
  protected List<T> result;
}

Et puis testé avec ce code:

  Response<Project> responseProject = unmarshal(unmarshaller, Project.class, "responseProject.xml");
  System.out.println(responseProject.getStatus());
  for (Project project:responseProject.getResult()) System.out.println(project);

private static <T> Response<T> unmarshal(Unmarshaller unmarshaller, Class<T> clazz, String xmlLocation) throws JAXBException {
  StreamSource xml = new StreamSource(xmlLocation);
  @SuppressWarnings("unchecked")
  Response<T> wrapper = (Response<T>) unmarshaller.unmarshal(xml, Response.class).getValue();
  return wrapper;
}

Et je bénéficier de cette exception, la lecture de la réponse de la liste:

Exception in thread "main" java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to org.test.Project

Note: je ne peux pas modifier le XML d'origine. Il y a plus d'autres types de Projet et de l'Utilisateur.

L'exemple suivant permettra de: blog.bdoughan.com/2012/11/...
Bonjour Blaise, j'ai déjà trouvé votre article, nice btw, mais j'obtiens une exception d'essayer de lire la liste: Exception in thread "main" java.lang.ClassCastException: com.soleil.org.apache.xerces.interne.dom.ElementNSImpl ne peut pas être lancé pour org.test.Projet, je mettrai à jour la question avec l'essayé la solution.
Vous devez vous assurer que le JAXBContext est conscient de toutes les classes, et que chacun des éléments de la collection est annoté avec @XmlRootElement.
Essayez de triage d'une instance de l'objet de votre modèle. Puis de le comparer avec l'entrée que vous utilisez pour voir si elles correspondent?
Fait ce travail, je vous remercie! Je vais poster une réponse.

OriginalL'auteur Fedy2 | 2013-11-09