java - 如何教导 Apache CXF JAX-WS 客户端使用的 JAXB 解码非类型化属性中的 {http ://microsoft. com/wsdl/types/}guid 值?

标签 java jaxb cxf jax-ws soap-client

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

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

WSDL 没有提及任何 GUID,并且看起来完全是自给自足的。然而,有一个字段(名为 Value )是无类型的,即。 e.一个xsd:element没有type属性,服务器发送响应时包含该字段中的各种类型的值。它们看起来像这样:

  • <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>
    

字符串和短裤都可以,但 GUID 在客户端上会产生此异常:

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

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

我尝试过的

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

  • 添加<jaxb:binding><jaxb:property><jaxb:baseType>到我的自定义绑定(bind) XML,使其将字段视为字符串 - 它使 Java 属性成为字符串,但显然仍然根据指定类型对数据进行解码,并且由于它无法将日期转换为字符串而损坏;

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

  • 手动添加 one 中的类型定义的these sources :

    <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:element 后除了 xsd:simpleType ,我终于得到了wsdl2javaObjectFactory 上生成方法注释为 @XmlElementDecl(namespace = "http://microsoft.com/wsdl/types/", name = "guid") ,但是这个方法还是没有用,简单String在我的 WSDL 提到 guid 的地方仍然使用,以及UnmarshalException坚持;

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

    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)注释到您的 wsdl2java 之一-生成的类已经被 JAXB 拾取,例如。 G。实际的服务等级。服务类可能已经有 @XmlSeeAlso({ObjectFactory.class}) ,所以你可以将其更改为 @XmlSeeAlso({ObjectFactory.class, Guid.class}) .

这样,JAXB 将成功将 GUID 解码为 Guid具有纯字符串内容的实例。如果你想要实际的java.util.UUID实例,您可以添加 @XmlJavaTypeAdapter关于@XmlValue领域,但我还没有测试过。

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

关于java - 如何教导 Apache CXF JAX-WS 客户端使用的 JAXB 解码非类型化属性中的 {http ://microsoft. com/wsdl/types/}guid 值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53884106/

相关文章:

java - 从旧MySQL数据库迁移数据

java.lang.NoClassDefFoundError : org. apache.commons.lang3.StringUtils

java - JAXB @XmlElements,不同类型但名称相同?

web-services - 从 Web 服务获取 HTTP session

java - 在二维矩阵中找到最左边和最右边角之间的最短路径

java - java中归并排序的问题

java - 在解码操作期间将 nil ="true"转换为 null

java - 如何使用 jaxb 获取 XML 格式的标签之间的值

java - 扩展泛型类并第二次实现其接口(interface)

java - 从端口的方法返回一个类而不是 void