自定义SOAP响应的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.

考虑问候你好世界的例子,我想要这个:

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开始并生成所有各种JAXB使用 wsimport 注释请求和响应包装类,然后在生成的包中找到 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 $ c $来阻止它覆盖您修改的 package-info c>选项 xjc (这告诉它不要生成 package-info.java 而是要把所有的必要的名称空间类级别注释的设置)。具体如何做到这一点取决于你如何运行 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 ....

Ant:

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

Maven:

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