如何教Apache CXF JAX-WS客户端使用的JAXB解组{http://microsoft.com/wsdl/t



我有一个SOAP服务需要向其发送请求(特别是Ivanti Integration Web service)。

我使用Apache CXF 3.2.7连接到该服务。我使用wsdl2java从服务的WSDL生成Java类。

WSDL没有提及任何GUID,而且似乎完全自给自足。然而,有一个字段(名为Value)是非类型化的,即不带type属性的xsd:element,服务器会在该字段中发送具有各种类型值的响应。它们看起来像这样:

  • <Value xsi:type="xsd:string">foobar</Value>
    
  • <Value xsi:type="xsd:short">1</Value>
    
  • <Value xsi:type="q2:guid" xmlns:q2="http://microsoft.com/wsdl/types/">c3aca40a-439d-4af2-b42e-59b1ddcf3d6e</Value>
    

字符串和short都可以,但GUID在客户端上产生以下异常:

javax.xml.bind.UnmarshalException: unrecognized type name: {http://microsoft.com/wsdl/types/}guid

如何避免此异常我实际上并不关心这个字段的值,尽管实现类型安全解组的解决方案当然是理想的。

我尝试过的

无论我做什么,例外都不会消失。特别是,我尝试过:

  • <jaxb:binding><jaxb:property><jaxb:baseType>添加到我的自定义绑定XML中,使其将字段视为字符串——它使Java属性成为字符串,但显然一直根据指定的类型对数据进行解组,并由于无法将日期转换为字符串而中断;

  • 使用自定义解组方法添加<jaxb:javaType><jxc:javaType>——这根本不起作用,wsdl2java失败,无论我将元素放在哪里,也无论我指定了什么Java类型,都会显示"编译器无法执行此转换自定义。它附加到了错误的位置,或者它与其他绑定不一致";

  • 手动添加来自以下来源之一的类型定义:

    <xs:schema elementFormDefault="qualified" targetNamespace="http://microsoft.com/wsdl/types/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:simpleType name="guid">
    <xs:restriction base="xs:string">
    <xs:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>
    </xs:restriction>
    </xs:simpleType>
    </xs:schema>
    

    在调用服务的wsdl2java之前,将其添加到服务的WSDL文件中——在xsd:simpleType之外添加了一个xsd:element之后,我终于让wsdl2javaObjectFactory上生成了一个用@XmlElementDecl(namespace = "http://microsoft.com/wsdl/types/", name = "guid")注释的方法,但这个方法仍然没有使用,在我的WSDL引用guid的地方,仍然使用普通的String,并且UnmarshalException保持不变;

  • 甚至在USER_STREAM阶段添加一个in-Interceptor,将整个InputStream消耗成一个字符串,残酷地找到所有看起来像GUIDxsi:type/xmlns:q2属性的东西,并用类似于这个答案的xsi:type="xsd:string"替换它们——但我一定犯了一些错误,因为异常仍然没有消失;这是我的代码:

    import java.io.ByteArrayInputStream;
    import java.io.InputStream;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import org.apache.commons.io.IOUtils;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    public class GuidExpungeInterceptor extends AbstractPhaseInterceptor<Message> {
    private static class GuidExpungedInputStream extends ByteArrayInputStream {
    private final InputStream stream;
    public GuidExpungedInputStream(InputStream stream) throws IOException {
    super(guidExpungedByteArray(stream));
    this.stream = stream;
    }
    private static byte[] guidExpungedByteArray(InputStream stream) throws IOException {
    String content = IOUtils.toString(stream, StandardCharsets.ISO_8859_1);
    content = content.replaceAll("<Value xsi:type="([A-Za-z_][A-Za-z0-9_.-]*):guid" xmlns:\1="http://microsoft.com/wsdl/types/">", "<Value xsi:type="xsd:string">");
    return content.getBytes(StandardCharsets.ISO_8859_1);
    }
    @Override
    public void close() throws IOException {
    stream.close();
    super.close();
    }
    }
    public GuidExpungeInterceptor() {
    super(Phase.USER_STREAM);
    }
    @Override
    public void handleMessage(Message message) {
    if (message == message.getExchange().getInMessage()) {
    try {
    InputStream stream = message.getContent(InputStream.class);
    message.setContent(InputStream.class, new GuidExpungedInputStream(stream));
    } catch (IOException e) {
    throw new Fault(e);
    }
    }
    }
    }
    
    class BlahController {
    BlahController() {
    JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
    proxyFactory.setServiceClass(FRSHEATIntegrationSoap.class);
    proxyFactory.setAddress(this.properties.getFrsHeatIntegrationUrl());
    this.service = (FRSHEATIntegrationSoap) proxyFactory.create();
    Client client = ClientProxy.getClient(service);
    client.getInInterceptors().add(new GuidExpungeInterceptor());
    }
    }
    

    然后我使用this.service来调用强类型操作方法。也许拦截器没有保留在本地client变量之外?

如果我理解正确(我一点也不确定),这个异常意味着JAXB没有为GUID类型注册的解组器,如果我能以某种方式保留JAXB注册表并添加我自己的整理器,这个问题应该得到解决。但是在看了CXF的JavaDocs之后,我不知道如何,甚至不知道是否可以访问这个注册表。有些方法听起来好像我可以获得JAXBContext,但我不知道如何向现有的JAXBContext实例添加任何内容。

如果将wsdl2java从原始WSDL生成的源代码导入源代码管理(并在每次构建时停止生成),则可以添加一个自定义类映射simpleType:

import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@XmlType(namespace = "http://microsoft.com/wsdl/types/", name = "guid")
public class Guid {
@XmlValue
public String guid;
}

并将@XmlSeeAlso(Guid.class)注释添加到已经被JAXB获取的wsdl2java生成的类之一,例如实际的服务类。服务类可能已经有了@XmlSeeAlso({ObjectFactory.class}),所以您可以将其更改为@XmlSeeAlso({ObjectFactory.class, Guid.class})

这样,JAXB将成功地将GUID解组为具有纯字符串内容的Guid实例。如果您想要实际的java.util.UUID实例,您可以在@XmlValue字段中添加@XmlJavaTypeAdapter,但我还没有对此进行测试。

(顺便说一句:当您尝试将xsd:element添加到WSDL时,我认为您添加了一个名为guidXML元素的映射,例如<q2:guid xmlns:q2="http://microsoft.com/wsdl/types/">c3aca40a-439d-4af2-b42e-59b1ddcf3d6e</q2:guid>。这不是您想要的,所以这解释了为什么它对您没有帮助。)

相关内容

最新更新