在JAXB中,在解组过程中忽略未定义的元素引用

huangapple go评论65阅读模式
英文:

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,以确定如何对对象进行marshalunmarshal

例如,可以实现如下所示的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&lt;Object, Person1&gt; {

    @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(&quot;http://www.w3.org/2001/XMLSchema&quot;, &quot;ref&quot;);
/**
* 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&lt;Attribute&gt; attributes = new ArrayList&lt;&gt;();
startElement.getAttributes().forEachRemaining(a -&gt; {
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);

huangapple
  • 本文由 发表于 2020年10月2日 23:03:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/64173852.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定