I need to load an XML files, but there exists two identical formats of the file, save for the namespace being different - in my simplified example,
apple:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="apple">
</ns2:container>
pear:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="pear">
</ns2:container>
The XmlRootElement references a specific namespace, and so I can't process both files the same way:
public class NamespaceTest {
@XmlRootElement(namespace = "apple")
public static class Container {
}
public static void main(final String[] args) throws Exception {
// Correct namespace - works
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="apple">
</ns2:container>
""");
// Incorrect namespace - doesn't work
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="pear">
</ns2:container>
""");
}
private static void unmarshall(final String xml) throws Exception {
try (Reader reader = new StringReader(xml)) {
System.out.println(JAXBContext.newInstance(Container.class).createUnmarshaller().unmarshal(reader));
}
}
}
}
Gives the output:
com.my.app.NameSpaceTest$Container@77167fb7
Exception in thread "main" javax.xml.bind.UnmarshalException: unexpected element (uri:"pear", local:"container"). Expected elements are <{apple}container>
At the moment I've got this working in a sub-optimal way by modifying the data as it's being read, using https://stackoverflow.com/a/50800021 - but I'd like to move this into JAXB if possible.
public class NameSpaceTest {
@XmlRootElement(namespace = "apple")
public static class Container {
}
public static void main(final String[] args) throws Exception {
// Correct namespace
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="apple">
</ns2:container>
""");
// Incorrect namespace
unmarshall("""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="pear">
</ns2:container>
""");
}
private static void unmarshall(final String xml) throws Exception {
try (Reader reader = new TranslatingReader(new BufferedReader(new StringReader(xml))) {
@Override
public String translate(final String line) {
return line.replace("pear", "apple");
}
}) {
System.out.println(JAXBContext.newInstance(Container.class).createUnmarshaller().unmarshal(reader));
}
}
/** @see <a href="https://stackoverflow.com/a/50800021">Source</a> */
private abstract static class TranslatingReader extends Reader {
private final BufferedReader input;
private StringReader output = new StringReader("");
public TranslatingReader(final BufferedReader input) {
this.input = input;
}
public abstract String translate(final String line);
@Override
public int read(final char[] cbuf, int off, int len) throws IOException {
int read = 0;
while (len > 0) {
final int nchars = output.read(cbuf, off, len);
if (nchars == -1) {
final String line = input.readLine();
if (line == null) {
break;
} else {
output = new StringReader(translate(line) + System.lineSeparator());
}
} else {
read += nchars;
off += nchars;
len -= nchars;
}
}
if (read == 0) {
read = -1;
}
return read;
}
@Override
public void close() throws IOException {
input.close();
output.close();
}
}
}
Output:
com.my.app.NameSpaceTest$Container@6ce139a4
com.my.app.NameSpaceTest$Container@18ce0030