定制 SOAP 响应的 JAX-WS 前缀

2022-01-19 00:00:00 soap java jaxb jax-ws

我正在为一个相当旧的(但遗憾的是不可更改的)接口实现 Web 服务.我遇到了一个问题,即调用我的服务的客户端需要 SOAP 响应中的某个命名空间,而我很难将其更改为匹配.

I'm implementing a web service for quite an old (but sadly unchangeable) interface. I have an issue where the client that is calling my service expects a certain namespace in the SOAP response and I'm having difficulty in changing it to match.

考虑一个 hello world 示例,我想要这个:

Considering a hello world example, I want this:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:helloResponse xmlns:ns2="http://test/">
         <return>Hello Catchwa!</return>
      </ns2:helloResponse>
   </S:Body>
</S:Envelope>

看起来像这样:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <customns:helloResponse xmlns:customns="http://test/">
         <return>Hello Catchwa!</return>
      </customns:helloResponse>
   </S:Body>
</S:Envelope>

我在这里找到了类似于我正在尝试做的事情,但我无法获得类似的代码正确执行.(我想坚持使用 Metro 而不必更改为 cxf 或轴)

I found something similar to what I'm trying to do here but I'm having trouble getting similar code to execute properly. (I'd like to stick with Metro and not have to change to cxf or axis)

我的实现JAXBContextFactory 返回 JAXBRIContext 看起来像这样:

My implementation of JAXBContextFactory that returns a JAXBRIContext looks like this:

import com.sun.xml.bind.api.JAXBRIContext;
import com.sun.xml.bind.api.TypeReference;
import com.sun.xml.ws.api.model.SEIModel;
import com.sun.xml.ws.developer.JAXBContextFactory;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;

public class HelloJaxbContext implements JAXBContextFactory
{
  @Override
  public JAXBRIContext createJAXBContext(SEIModel seim, List<Class> classesToBind, List<TypeReference> typeReferences) throws JAXBException {
    List<Class> classList = new ArrayList<Class>();
    classList.addAll(classesToBind);

    List<TypeReference> refList = new ArrayList<TypeReference>();
    for (TypeReference tr : typeReferences) {
        refList.add(new TypeReference(new QName(tr.tagName.getNamespaceURI(), tr.tagName.getLocalPart(), "customns"), tr.type, tr.annotations));
    }
    return JAXBRIContext.newInstance(classList.toArray(new Class[classList.size()]), refList, null, seim.getTargetNamespace(), false, null);
  }  
}

Web 服务的一些测试代码很简单:

Some test code for the web service is simply:

import com.sun.xml.ws.developer.UsesJAXBContext;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;

@WebService(serviceName = "Hello")
@UsesJAXBContext(value = HelloJaxbContext.class)
public class Hello
{
  @WebMethod(operationName = "hello")
  public String hello(@WebParam(name = "name") String txt)
  {
    return "Hello " + txt + "!";
  }
}

<小时>

问题

在使用 jaxws-rt 2.2.7(来自 Maven)的 Tomcat 7.0.32 和 Glassfish 3.1.2 中,上述代码不会影响我的 Web 服务输出(命名空间前缀仍然是ns2").


Issues

In Tomcat 7.0.32 and Glassfish 3.1.2 using jaxws-rt 2.2.7 (from Maven), the above code doesn't affect my web service output (the namespace prefix is still "ns2").

推荐答案

如果您从旧服务的 WSDL 开始,并使用 wsimport 生成所有各种带有 JAXB 注释的请求和响应包装类,那么在生成的包中你应该找到一个 package-info.java 例如

If you started from the WSDL of the old service and generated all the various JAXB annotated request and response wrapper classes using wsimport, then in the generated package you should find a package-info.java such as

@javax.xml.bind.annotation.XmlSchema(namespace = "http://test/")
package com.example.test;

JAXB 为您提供了一种机制,可以在 @XmlSchema 注释上建议前缀映射,因此您可以尝试修改 package-info.java 以读取

JAXB provides a mechanism for you to suggest prefix mappings on the @XmlSchema annotation, so you could try modifying package-info.java to read

@javax.xml.bind.annotation.XmlSchema(namespace = "http://test/",
   xmlns = { 
      @javax.xml.bind.annotation.XmlNs(prefix = "customns", 
         namespaceURI="http://test/")
   }
)
package com.example.test;

看看这对生成的消息是否有任何影响.这还具有纯 JAXB 规范的优势(即不依赖于 RI 特定的自定义上下文工厂).

and see if that makes any difference to the generated messages. This also has the advantage of being pure JAXB spec (i.e. not dependent on the RI-specific custom context factory).

如果您需要重新运行 wsimport,您可以通过将 -npa 选项传递给xjc (这告诉它不要生成 package-info.java 而是将所有必要的 namespace 设置放在类级注释上反而).具体如何执行此操作取决于您运行 wsimport 的方式.

If you need to re-run wsimport you can prevent it from overwriting your modified package-info by passing the -npa option to xjc (this tells it not to generate a package-info.java but instead to put all the necessary namespace settings on the class-level annotations instead). Exactly how you do this depends how you're running wsimport.

命令行:

wsimport -B-npa ....

蚂蚁:

<wsimport wsdl="..." destdir="..." .... >
  <xjcarg value="-npa" />
</wsimport>

马文:

<plugin>
  <groupId>org.jvnet.jax-ws-commons</groupId>
  <artifactId>jaxws-maven-plugin</artifactId>
  <version>2.2</version>
  <executions>
    <execution>
      <goals>
        <goal>wsimport</goal>
      </goals>
      <configuration>
        <xjcArgs>
          <xjcArg>-npa</xjcArg>
        </xjcArgs>
      </configuration>
    </execution>
  </executions>
</plugin>

相关文章