我正在尝试使用
jakarta.xml.bind.Unmarshaller
将 XML 解组为 Java 对象,但我的 XML 不包含某些默认名称空间,因为它们已经是 XSD 的一部分。如何明确提及 Unmarshaller
以忽略这些默认命名空间或在运行时将它们添加到 XMLStreamReader
以便 Unmarshaller 可以使用它们?
以下是未能解组到 Java 对象的 XML:
<foo>
<bar>Hello</bar>
<test:one>Hello ONE</test:one>
</foo>
如果我更改为这个,那么它将按预期工作:
<foo>
<bar>Hello</bar>
<test:one xmlns:test="https://test.com">Hello ONE</test:one>
</foo>
我在 StackOverflow 上查看了一些示例,但提供的方法不起作用。我不想使用额外的库(例如 Sax),而是想在 XMLStreamReader 级别或使用 Unmarshaller 进行处理。
如何实现这一目标?
以下是对 XML 进行解组的方法:
主班:
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();
}
}
在没有命名空间的情况下使用XML时发生错误:
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
基于很少的响应,我尝试添加附加类来返回名称空间,但这也不起作用 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 "";
}
}
然后在 Main.class 中使用这些流:
final XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(inputStream);
NamespaceIgnoringXMLStreamReader namespaceIgnoringReader = new NamespaceIgnoringXMLStreamReader(xmlStreamReader);
我尝试使用以下代码添加
package-info.java
,但仍然没有命名空间的解组对我不起作用:
@XmlSchema(elementFormDefault = XmlNsForm.UNQUALIFIED)
package io.openepcis.convert.test;
import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;
这也让我绞尽脑汁,因为我有一个发布者将来自 XML 片段的消息拼凑在一起,其中一些片段来自 Oracle DB,它吐出 XMLType,并且这些片段不存储在具有命名空间的 DB 中。它曾经可以与 javax 一起使用,但现在不行了。
最简单的答案是,如果您有一个用于生成所有 @XmlType 类的 .xsd 文件,请将 elementFormDefault 设置为“unqualified”。这将允许您使用将声明的类型作为其参数之一的解组器。
这里有一些带有模式片段的示例:https://tomee.apache.org/jakartaee-10.0/javadoc/index.html?jakarta/xml/bind/Unmarshaller.html
如果您的架构将 elementFormDefault 设为“合格”并且您无法更改它(这就是我的情况),那么我仍在寻找答案。