英文:
Ignoring undefined element references during unmarshalling in JAXB
问题
我目前正在使用CXF框架开发一个请求Web服务的项目。
由于某些原因,我开始收到无效的XML SOAP(缺少从中引用的ID的元素)响应,导致在将其解组为POJO实例的过程中引发异常。
示例:
XML摘录,其中属性ref
引用了XML中不存在的标识符为Person1
的元素。
<ext:Applicant s:ref="Person1"/>
其中ref
在XSD架构中的类型为IDREF
:
<attribute name="ref" type="IDREF"/>
JAXB引发的异常:
javax.xml.ws.soap.SOAPFaultException: 解组错误:未定义的ID“Person1”。带有根本原因的]
javax.xml.bind.UnmarshalException: 未定义的ID“Person1”。
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:744)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.errorUnresolvedIDREF(UnmarshallingContext.java:795)
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$IDREFTransducedAccessorImpl$1.run(TransducedAccessor.java:330)
...
是否有办法让JAXB忽略缺少的引用,在解组传入的XML响应时不抛出异常?
英文:
I'm currently working on a project requesting web services using CXF framework.
For some reasons, I started to receive invalid XML SOAP(missing element with ID referenced from ) response that results in throwing exception during unmarshalling to POJO instance.
Example:
XML excerpt where attribute ref
references to an element with identifier Person1
that does not exist in XML.
<ext:Applicant s:ref="Person1"/>
where ref
is IDREF
type in XSD schema
<attribute name="ref" type="IDREF"/>
Exception thrown by JAXB
javax.xml.ws.soap.SOAPFaultException: Unmarshalling Error: Undefined ID "Person1". ] with root cause
javax.xml.bind.UnmarshalException: Undefined ID "Person1".
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:744) ~[jaxb-impl-2.3.1.jar!/:2.3.1]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.errorUnresolvedIDREF(UnmarshallingContext.java:795) ~[jaxb-impl-2.3.1.jar!/:2.3.1]
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$IDREFTransducedAccessorImpl$1.run(TransducedAccessor.java:330) ~[jaxb-impl-2.3.1.jar!/:2.3.1]
at...
Is there a way how to make JAXB ignore missing references and unmarshall incoming XML response without throwing exception?
答案1
得分: 1
以下是翻译好的部分:
XML对象未在请求中提供,以更具体地讨论。有一个方便的解决方法,可以为JAX-B对象创建CustomAdapter
,以确定如何对对象进行marshal
和unmarshal
。
例如,可以实现如下所示的CustomAdapter
:
public static class CustomAdapter extends XmlAdapter<Object, Person1> {
@Override
public Object marshal(Person1 value) {
// 您的编组实现
}
@Override
public Person1 unmarshal(Object value) {
// 您的解组实现
}
}
XmlAdapter
位于javax.xml.bind.annotation.adapters.XmlAdapter
包中。
如果您将Person1
用作其他对象(组合)中的字段,则可以像下面这样用@XmlJavaTypeAdapter
进行注释:
@XmlJavaTypeAdapter(CustomAdapter.class)
private Person1 person1;
如果有帮助,请告诉我。
英文:
The XML object is not provided in the request to discuss more specifically. There is a handy workaround to create CustomAdapter
for JAX-B objects to determine how to marshal
and unmarshal
the object.
As an example, the below CustomAdapter
could be implemeted:
public static class CustomAdapter extends XmlAdapter<Object, Person1> {
@Override
public Object marshal(Person1 value) {
// your implementation to marshal
}
@Override
public Person1 unmarshal(Object value) {
// your implementation to unmarshal
}
}
XmlAdapter
is in javax.xml.bind.annotation.adapters.XmlAdapter
package.
If you are using Person1
as a field in other objects (composition), you could make it annotated with @XmlJavaTypeAdapter
as below:
@XmlJavaTypeAdapter(CustomAdapter.class)
private Person1 person1;
Please let me know if it helps out.
答案2
得分: 0
可能的解决方案可以是为XMLEventReader创建一个装饰器,用于过滤掉IDREF属性(或其他必要的属性):
public class IdRefFilteringReader implements XMLEventReader {
/**
* 需要移除的属性的QName
*/
private final static QName QNAME = new QName("http://www.w3.org/2001/XMLSchema", "ref");
/**
* 委托的XML事件阅读器
*/
private final XMLEventReader delegate;
/**
* XML事件工厂
*/
private final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
/**
* 构造函数注入委托
*/
public IdRefFilteringReader(XMLEventReader delegate) {
this.delegate = delegate;
}
/**
* 移除具有匹配QName的属性
*/
@Override
public XMLEvent nextEvent() throws XMLStreamException {
XMLEvent event = delegate.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
Attribute attr = startElement.getAttributeByName(QNAME);
// 如果属性存在,创建一个新的XMLEvent,其前缀、命名空间、名称以及其他属性都相同,
// 唯独要移除一个属性
if(attr != null) {
String prefix = startElement.getName().getPrefix();
String uri = startElement.getName().getNamespaceURI();
String localname = startElement.getName().getLocalPart();
List<Attribute> attributes = new ArrayList<>();
startElement.getAttributes().forEachRemaining(a -> {
if(!a.getName().equals(attr.getName())) {
attributes.add(a);
}
});
return eventFactory.createStartElement(
prefix,
uri,
localname,
attributes.iterator(),
startElement.getNamespaces()
);
}
}
return event;
}
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public XMLEvent peek() throws XMLStreamException {
return delegate.peek();
}
@Override
public String getElementText() throws XMLStreamException {
return delegate.getElementText();
}
@Override
public XMLEvent nextTag() throws XMLStreamException {
return delegate.nextTag();
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
return delegate.getProperty(name);
}
@Override
public void close() throws XMLStreamException {
delegate.close();
}
@Override
public Object next() {
return delegate.next();
}
}
要使用这个阅读器,需要将其传递给unmarshaller实例,例如:
// 创建unmarshaller
JAXBContext ctx = JAXBContext.newInstance(Applicant.class);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
// 创建常规的XML事件阅读器和经过过滤的XML事件阅读器
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLEventReader reader = xif.createXMLEventReader(new StreamSource(new StringReader(xml)));
XMLEventReader filteringReader = new IdRefFilteringReader(reader);
// 使用过滤的阅读器进行XML解组
Applicant applicant = unmarshaller.unmarshal(filteringReader, Applicant.class);
英文:
Possible solution could be creating a decorator for XMLEventReader, which will filter out the IDREF attributes (or other attributes if needed):
public class IdRefFilteringReader implements XMLEventReader {
/**
* QName of the attribute to be removed
*/
private final static QName QNAME = new QName("http://www.w3.org/2001/XMLSchema", "ref");
/**
* Delegate XML event reader
*/
private final XMLEventReader delegate;
/**
* XML event factory
*/
private final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
/**
* Constructor injects delegate
*/
public IdRefFilteringReader(XMLEventReader delegate) {
this.delegate = delegate;
}
/**
* Remove attributes with matching QName
*/
@Override
public XMLEvent nextEvent() throws XMLStreamException {
XMLEvent event = delegate.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
Attribute attr = startElement.getAttributeByName(QNAME);
// if attribute is present, create new XMLEvent with same
// prefix, namespace, name and other attributes except one
// which should be removed
if(attr != null) {
String prefix = startElement.getName().getPrefix();
String uri = startElement.getName().getNamespaceURI();
String localname = startElement.getName().getLocalPart();
List<Attribute> attributes = new ArrayList<>();
startElement.getAttributes().forEachRemaining(a -> {
if(!a.getName().equals(attr.getName())) {
attributes.add(a);
}
});
return eventFactory.createStartElement(
prefix,
uri,
localname,
attributes.iterator(),
startElement.getNamespaces()
);
}
}
return event;
}
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public XMLEvent peek() throws XMLStreamException {
return delegate.peek();
}
@Override
public String getElementText() throws XMLStreamException {
return delegate.getElementText();
}
@Override
public XMLEvent nextTag() throws XMLStreamException {
return delegate.nextTag();
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
return delegate.getProperty(name);
}
@Override
public void close() throws XMLStreamException {
delegate.close();
}
@Override
public Object next() {
return delegate.next();
}
}
To use this reader it's needed to pass it to unmarshaller instance, e.g.:
// create unmarshaller
JAXBContext ctx = JAXBContext.newInstance(Applicant.class);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
// create regular XML event reader and filtered XML event reader
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLEventReader reader = xif.createXMLEventReader(new StreamSource(new StringReader(xml)));
XMLEventReader filteringReader = new IdRefFilteringReader(reader);
// unmarshall XML using filtering reader
Applicant applicant = unmarshaller.unmarshal(filteringReader, Applicant.class);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论