Ciao a tutti,
sto interrogando diversi endpoint di un'API, ognuno dei quali restituisce un XML diverso.
Gli XML sono molto simili tra di loro ad eccezione di 2 elementi. Di seguito un esempio semplificato (nella realtà sono molto più complessi, ma gli esempi si focalizzano sul problema che sto riscontrando):
User.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<header>
<userAPI info="User info"/>
</header>
<userAPI>
<username>charles</username>
</userAPI>
</root>
Order.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<header>
<orderAPI info="Order info"/>
</header>
<orderAPI>
<orderId>1</orderId>
</orderAPI>
</root>
Visto la similitudine tra i vari XML, il mio obiettivo è quello di evitare del codice ridondante per l'unmarshalling di questi file.
Sono riuscito (in parte) ad ottenere quanto desidero con l'utilizzo di classi generiche, ma sto riscontrando dei problemi legati al fatto che ogni file ha due elementi con lo stesso nome (un figlio del nodo header, un figlio del nodo root):
- userAPI per il file User.xml
- orderAPI per il file Order.xml
Di seguito sono riportate le classi implementate (utilizzando Lombok) per eseguire l'unmarshalling del file User.xml
pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
UserApiHeader.java
@Data
@XmlRootElement(name = "userAPI")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserApiHeader {
@XmlAttribute
private String info;
}
UserApi.java
@Data
@XmlRootElement(name = "userAPI")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserApi {
@XmlElement
private String username;
}
Header.java
@Data
@XmlRootElement(name = "header")
@XmlAccessorType(XmlAccessType.FIELD)
public class Header<T> {
@XmlAnyElement(lax = true)
private T headerDetail;
}
Root.java
@Data
@XmlRootElement(name = "root")
@XmlAccessorType(XmlAccessType.FIELD)
public class Root<T, E> {
@XmlElement
private Header<T> header;
@XmlAnyElement(lax = true)
private E body;
}
Main.java
public class Main {
public static void main(String[] args) {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
+ "<root>\r\n"
+ " <header>\r\n"
+ " <userAPI info=\"User info\"/>\r\n"
+ " </header>\r\n"
+ " <userAPI>\r\n"
+ " <username>charles</username>\r\n"
+ " </userAPI>\r\n"
+ "</root>";
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class, UserApi.class, UserApiHeader.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
@SuppressWarnings("unchecked")
Root<UserApiHeader, UserApi> root = (Root<UserApiHeader, UserApi>) unmarshaller.unmarshal(new StringReader(xml));
System.out.println("root.header.userAPI: " + root.getHeader().getHeaderDetail());
System.out.println("root.userAPI: " + root.getBody());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Questo è risultato stampato in console
root.header.userAPI: UserApiHeader(info=User info)
root.userAPI: UserApiHeader(info=null)
Dal risultato della console, si evince che l'unmarshaller associa la classe UserApiHeader ad entrambi i nodi userAPI; riuscendo a deserializzare il nodo root.header.userAPI ma non il nodo root.userAPI.
Provando ad invertire le classi UserApi e UserApiHeader nell'istanziare il JAXBContext, il problema si inverte:
public class Main {
public static void main(String[] args) {
// ...
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class, UserApiHeader.class, UserApi.class);
// ...
}
}
root.header.userAPI: UserApi(username=null)
root.userAPI: UserApi(username=charles)
Come dicevo, il problema si inverte: l'unmarshaller associa la classe UserApi ad entrambi i nodi userAPI, riuscendo a deserializzare il nodo root.userAPI ma non il nodo root.header.userAPI.
Il problema descritto non viene riscontrato modificando il nome di uno dei due nodi userAPI (cosa che nella realtà non posso fare in quanto ricevo i file così come sono). Esempio:
User.xml (solo parti modificate)
...
<header>
<userAPI_ info="User info"/>
</header>
...
UserApiHeader.java (solo parti modificate)
// ...
@XmlRootElement(name = "userAPI_")
// ...
rieseguendo il main, ottengo questo (indipendentemente dall'ordine delle classi nell'instanziare il JAXBContext):
root.header.userAPI: UserApiHeader(info=User info)
root.userAPI: UserApi(username=charles)
Qualcuno riesce ad aiutarmi?
Grazie mille in anticipo.