I’m querying several endpoints of an API that returns XML data.
The XML returned by the various endpoints are very similar to each other, with the exception of the name of one element (simplified examples below).
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>
I managed to implement classes using generics but I’m encountering a problem related to elements that have the same name, in fact, a peculiarity of these XMLs is that they always have 2 elements with the same name (one child of header
and one child of root
):
userAPI
for the User.xml fileorderAPI
for the Order.xml file
Below are the classes implemented (using Lombok) for unmarshaling the User.xml file
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"?>rn"
+ "<root>rn"
+ " <header>rn"
+ " <userAPI info="User info"/>rn"
+ " </header>rn"
+ " <userAPI>rn"
+ " <username>charles</username>rn"
+ " </userAPI>rn"
+ "</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("Header detail: " + root.getHeader().getHeaderDetail());
System.out.println("Body: " + root.getBody());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Result
Header detail: UserApiHeader(info=User info)
Body: UserApiHeader(info=null)
From the console result you can see that unmarshalling associates the UserApiHeader
class with both userAPI
elements, therefore it fails to deserialize the root.userAPI
element returning null
Another thing I noticed is that if I reverse the orders of the classes when instantiating the JAXBContext, unmarshalling associates the UserApi
class with both userAPI
, therefore it fails to deserialize the root.header.userAPI
element returning null
public class Main {
public static void main(String[] args) {
// ...
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class, UserApiHeader.class, UserApi.class);
// ...
}
}
Header detail: UserApi(username=null)
Body: UserApi(username=charles)
The problem described does not arise if I change the name of one of the two elements (which, however, I cannot actually do since, as anticipated, they are files that I receive from an API).
Any suggestions?
Thanks in advance