@XmlAnyElement 不会解组为特定的 Java 类型,而是在 JAXBElement 处停止

@XmlAnyElement does not unmarshal into specific Java type, but stop at JAXBElement

为了学习如何使用 @XmlAnyElement,我创建了以下测试服务:

@WebService(serviceName = "TestServices")
@Stateless()
public class TestServices {
    @WebMethod(operationName = "testMethod")
    public ServiceResult testMethod() {
        ServiceResult result = new ServiceResult();

        result.addObject(new SimpleObj(1, 2));
        result.addObject(new SimpleObj(3, 4));

        return result;
    }
}

SimpleObj 是一个简单的 class,有 2 个 int 字段。下面是 ServiceResult class:

的代码
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({SimpleObj.class})
public class ServiceResult {
    @XmlAnyElement(lax = true)
    private List<Object> body;

    public void addObject(Object objToAdd) {
        if (this.body == null)
            this.body = new ArrayList();

        this.body.add(objToAdd);
    }

    // Getters and Setters
}

为了使用上述服务,我创建了一个具有以下内容的 appclient Main class:

public class Main {
    @WebServiceRef(wsdlLocation = "META-INF/wsdl/localhost_8080/TestServices/TestServices.wsdl")
    private static TestServices_Service service;
    private static TestServices         port;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        port = service.getAdminServicesPort();
        ServiceResult result = port.testMethod();

        for (Object o : result.getAny()) {
            System.out.println("TEST: " + o);
        }
    }
}

根据文档,使用 @XmlAnyElement,解组器将急切地将此元素解组为 JAXB 对象。但是,我观察到 JAXB 仅将我的对象解析为 JAXBElement 而不是一直解析为 SimpleObj.

如果你能告诉我如何从 ServiceResult 中得到 SimpleObj,我将不胜感激。

更新:

下面是SimpleObj class:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SimpleObj {
    private int a;
    private int b;

    public SimpleObj() {}

    public SimpleObj(int a, int b) {
        this.a = a;
        this.b = b;
    }

    // Getters and Setters
}

我无法重现您遇到的问题。下面是一些直接与 JAXB 交互的演示代码。

import java.io.*;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(ServiceResult.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        StringReader xml = new StringReader("<serviceResult><simpleObj/><simpleObj/></serviceResult>");
        ServiceResult result = (ServiceResult) unmarshaller.unmarshal(xml);

        for(Object item : result.getBody()) {
            System.out.println(item.getClass());
        }
    }

}

运行 演示代码的输出显示它是 SimpleObj 在用 @XmlAnyElement(lax=true) 注释的字段中的实例。

class forum27871349.SimpleObj
class forum27871349.SimpleObj

更新 #1

On the side note, I've read your blog articles on @XmlAnyElement and I've never seen you had to include @XmlSeeAlso({SimpleObj.class}) in any of your examples.

我不确定为什么我从不在示例中使用 @XmlSeeAlso

However, in my case, if I don't have this, I would have the error saying Class *** nor any of its super class is known to this context. It'd be great if you could also show me if there is a way to make all of these classes known to the consumer without using @XmlSeeAlso

当您自己创建 JAXBContext 时,您只需将您在 @XmlSeeAlso 注释中引用的任何内容包含在您曾经 [=] 的 classes 中74=] JAXBContext.

JAXBContext jc = JAXBContext.newInstance(ServiceResult.class, SimpleObj.class);

在无法直接访问 JAXBContext 的 JAX-WS(或 JAX-RS)设置中,我建议像您一样使用 @XmlSeeAlso 注释。


更新#2

Regarding the @XmlAnyElement, from the documentation, I thought if the unmarshaller cannot unmarshal elements into JAXB objects or JAXBElement objects, I will at least get a DOM node.

当您将 属性 映射到 @XmlAnyElement(lax=true) 时,将发生以下情况:

  1. 如果该元素对应于 class 的 @XmlRootElement,那么您将获得该 class 的实例。
  2. 如果元素对应于 ObjectFactory 上的 class 的 @XmlElementDecl 或另一个用 @XmlRegistry 注释的 class 那么你将得到一个实例其中 class 包裹在 JAXBElement.
  3. 的实例中
  4. 如果 JAXB 没有元素与 class 之间的关联,那么它将把它转换为 DOM Element.

下面我会举例说明。

对象工厂

import javax.xml.namespace.QName;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;

@XmlRegistry
public class ObjectFactory {

    @XmlElementDecl(name="simpleObjJAXBElement")
    public JAXBElement<SimpleObj> createSimpleObj(SimpleObj simpleObj) {
        return new JAXBElement<SimpleObj>(new QName("simpleObjJAXBElement"), SimpleObj.class, simpleObj);
    }

}

演示

import java.io.*;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(ServiceResult.class, ObjectFactory.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        StringReader xml = new StringReader("<serviceResult><simpleObj/><unmapped/><simpleObjJAXBElement/></serviceResult>");
        ServiceResult result = (ServiceResult) unmarshaller.unmarshal(xml);

        for(Object item : result.getBody()) {
            System.out.println(item.getClass());
        }
    }

}

输出

class forum27871349.SimpleObj
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
class javax.xml.bind.JAXBElement