How to unmarshall a XML without namespace using the Jakarta XML Unmarshaller?

103 views Asked by At

I am trying to unmarshal an XML into a Java object using the jakarta.xml.bind.Unmarshaller but my XML does not contain some of the default namespaces as they are part of the XSD already. How to either explicitly mention to Unmarshaller to ignore these default namespaces or add them to the XMLStreamReader at the runtime so Unmarshaller can use them?

Following is the XML which fails to unmarshal to Java object:

<foo>
    <bar>Hello</bar>
    <test:one>Hello ONE</test:one>
</foo>

If I change to this then it would work as expected:

<foo>
    <bar>Hello</bar>
    <test:one xmlns:test="https://test.com">Hello ONE</test:one>
</foo>

I looked at a few examples on StackOverflow but the provided approach does not work. I do not wish to use an additional library such as Sax rather I want to handler at the XMLStreamReader level or by using the Unmarshaller.

How to achieve this?

Following is the method for doing the unmarshalling of XML:

Main.class:

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) throws XMLStreamException, JAXBException {
        final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
        inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
        final InputStream inputStream = Main.class.getResourceAsStream("/SampleXML.xml");
        final XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(inputStream);
        final JAXBContext jaxbContext = JAXBContext.newInstance("io.convert.test", Thread.currentThread().getContextClassLoader());
        final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        xmlStreamReader.next();

        while (xmlStreamReader.hasNext()) {
            Object event = unmarshaller.unmarshal(xmlStreamReader, Foo.class).getValue();
            System.out.println(event.toString());
        }
    }
}

Foo.class:


import jakarta.xml.bind.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "foo")
@XmlType(
        name = "Foo",
        propOrder = {
                "bar",
                "anyElements"
        },
        factoryClass = ObjectFactory.class,
        factoryMethod = "createFoo")
@ToString(callSuper = true)
public class Foo {
    private String bar;
    
    @XmlAnyElement(lax = true)
    private List<Object> anyElements;

    void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("Before Unmarshaller Callback");
        System.out.println(parent);
    }

    void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("Before Unmarshaller Callback");
        System.out.println(parent);
    }
}

ObjectFactory.class:

import jakarta.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public final class ObjectFactory {
  private ObjectFactory() {}
  public static Foo createFoo() {
    return new Foo();
  }
}

An error occurred when XML used without namespace:

Exception in thread "main" jakarta.xml.bind.UnmarshalException
 - with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - 4.0.2.v202306161219): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[3,15]
Message: http://www.w3.org/TR/1999/REC-xml-names-19990114#ElementPrefixUnbound?test&test:one]
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:1132)
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:471)
    at io
.convert.test.Main.main(Main.java:24)
Caused by: Exception [EclipseLink-25004] (Eclipse Persistence Services - 4.0.2.v202306161219): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document

Based on few response I tried adding the additional class to return the namespaces but that did not work either NamespaceIgnoringXMLStreamReader.clas

import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;

public class NamespaceIgnoringXMLStreamReader extends StreamReaderDelegate {
    public NamespaceIgnoringXMLStreamReader(XMLStreamReader reader) {
        super(reader);
    }
    @Override
    public String getAttributeNamespace(int arg0) {
        return "";
    }
    @Override
    public String getNamespaceURI() {
        return "";
    }
}

Then using those streams in Main.class:

final XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(inputStream);
NamespaceIgnoringXMLStreamReader namespaceIgnoringReader = new NamespaceIgnoringXMLStreamReader(xmlStreamReader);

I tried adding the package-info.java with the following code but still, the unmarshalling without a namespace does not work for me:

@XmlSchema(elementFormDefault = XmlNsForm.UNQUALIFIED)
package io.openepcis.convert.test;

import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;
0

There are 0 answers