eclipselink
eclipselink copied to clipboard
JAXB/Moxy @XmlPath(".") conflicts with XMLAdapter during the unmarshalling.
In short, I would like to perform the unmarshalling
as mentioned here but along with Map
I will have one more @XmlElement
. So one field is annotated with (Map field) @XmlPath(".")
and another field with (String field) @XmlElement
and then I would like to perform unmarshalling
.
I had opened this issue and was thinking somethings wrong with my code but after trying a lot of things and researching I found out that the issue is actually with the MOXY
.
Basically, if the MOXY
is used with @XmlPath(".")
for and Map<String, Object>
and if the XMLAdapter
has been used for marshaling
and unmarshalling
then the marshaling
will work fine as expected but the unmarshalling
fails with the following error:
jakarta.xml.bind.UnmarshalException
- with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - 3.0.0.v202012081010): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: java.lang.NullPointerException: Cannot invoke "org.eclipse.persistence.internal.oxm.mappings.UnmarshalKeepAsElementPolicy.isKeepUnknownAsElement()" because "keepAsElementPolicy" is null]
I was able to locate and ticket which was logged back in 2015 but its status is still NEW
and when I checked the code it seems like it has been not fixed yet. I am using the latest version of the Moxy 3.0.0
.
I tried to modify the code as mentioned within the ticket and then added that JAR
to my code but even that did not work.
I would kindly request you to please look into this issue and provide some solution. I have provided a simple example with code in the below comment
In short, I would like to perform the unmarshalling
as mentioned here but along with Map
I will have one more @XmlElement
. So one field is annotated with (Map field) @XmlPath(".")
and another field with (String field) @XmlElement
and then I would like to perform unmarshalling
.
I tried to modify the source code and then used the JAR
from the updated code but still, the issue has not been fixed.
Following the XML
that I am trying to read:
<Customer xmlns:google="https://google.com">
<name>Rise Against</name>
<age>2000</age>
<google:sub1>MyValue-1</google:sub1>
<google:sub2>MyValue-1</google:sub2>
</Customer>
Following is the Customer.class
that I have:
@JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "userExtensions"})
@XmlAccessorType(XmlAccessType.FIELD)
@NoArgsConstructor
@Getter
@Setter
@AllArgsConstructor
public class Customer extends Person {
private String name;
private String age;
@XmlPath(".")
@XmlJavaTypeAdapter(TestAdapter.class)
@XmlAnyElement
private Map<String, Object> userExtensions = new HashMap<>();
@JsonAnySetter
public void setUserExtensions(String key, Object value) {
System.out.println("Key : " + key + " Value : " + value);
userExtensions.put(key, value);
}
@JsonAnyGetter
@JsonSerialize(using = CustomExtensionsSerializer.class)
public Map<String, Object> getUserExtensions() {
return this.userExtensions;
}
}
The Marshalling
works as expected but during the unmarshalling
it fails to read the custom-element
that are provided such as google:sub1
and google:sub2
.
public class Unmarshalling {
public static void main(String[] args) throws JAXBException, XMLStreamException, FactoryConfigurationError, JsonProcessingException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("customer.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
final Customer customer = unmarshaller.unmarshal(xmlStreamReader, Customer.class).getValue();
System.out.println("Read XML : " + customer);
}
}
Using the updated code and JAR
(Updating the resultNode = null
):
The System.out
just prints the Name and Age
but it does not display the user extensions
that have been provided.
Using the normal code and JAR
(with resultNode = xPathNode.getAnyNode();
)
The System.out
just does not print anything. All the values are NULL including name and age
.
So basically there is some conflict
while using the @XmlPath(".")
and XMLAdapter
.
I would kindly request you to look into this issue and provide some workaround or suggestions.
Following is my Adapter
:
public class TestAdapter extends XmlAdapter<Wrapper, Map<String, Object>> {
@Override
public Map<String, Object> unmarshal(Wrapper value) throws Exception {
System.out.println("ADAPTER UNMARSHALLING");
System.out.println(value.getElements());
if (value == null) {
return null;
}
//Loop across all elements within ILMD tag
final Map<String, Object> extensions = new HashMap<>();
for (Object obj : value.getElements()) {
Element element = (Element) obj;
//System.out.println("Node Name : " + element.getNodeName() + " Value : " + element.getTextContent());
final NodeList children = element.getChildNodes();
if (children.getLength() == 1) {
extensions.put(element.getNodeName(), element.getTextContent());
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Wrapper wrapper = new Wrapper();
List childElements = new ArrayList();
childElements.add(n);
wrapper.elements = childElements;
child.add(unmarshal(wrapper));
}
}
extensions.put(element.getNodeName(), child);
}
}
return extensions;
}
@SuppressWarnings("unchecked")
@Override
public Wrapper marshal(Map<String, Object> v) throws Exception {
if (v == null) {
return null;
}
Wrapper wrapper = new Wrapper();
List elements = new ArrayList();
for (Map.Entry<String, Object> property : v.entrySet()) {
if (property.getValue() instanceof Map) {
elements.add(new JAXBElement<Wrapper>(new QName(property.getKey()), Wrapper.class, marshal((Map) property.getValue())));
} else {
elements.add(new JAXBElement<String>(new QName(property.getKey()), String.class, property.getValue().toString()));
}
}
wrapper.elements = elements;
return wrapper;
}
}
class Wrapper {
@XmlAnyElement
List elements;
public List getElements() {
return elements;
}
}
Hello, in the attached test case is possible solution/workaround for this issue. There is used following declaration
...
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "userExtensions"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private String name;
private String age;
@XmlAnyElement(value = TestDomHandler.class)
private Map<String, Object> userExtensions = new HashMap<>();
...
Instead of @XmlPath(".")
and @XmlJavaTypeAdapter
is @XmlAnyElement(value = TestDomHandler.class)
used.
com.oracle.moxy.bugtest.domain.TestDomHandler.getElement()
contains additional XML parsing logic to parse and map other elements than <name>
and <age>
into private Map<String, Object> userExtensions
.
Please check attached Maven project is this solution suits to You.
moxy-bug-1181-JAXBMoxyXmlPathConflictsWithXMLAdapterDuringUnmarshallingSolution10.tar.gz