如何使用xsd:任何元素将xml作为字符串返回

问题描述:

我需要复制由Oracle生成并部署在Oracle上的旧Web Service。新服务将用Java编写并部署在Tomcat上。原始服务的所有功能必须完全相同,这意味着我无法更改wsdl或响应对象的格式。如何使用xsd:任何元素将xml作为字符串返回

我使用cxf和wsdl2java来生成存根类和服务。我的问题是,原始的wsdl使用“any”元素,并且响应中有异常的xml代码填充到该元素中。我似乎无法重现与原始服务相同的回应。

WSDL中的请求和响应类型定义:

 <complexType name="getNewServiceXML"> 
      <sequence> 
       <element name="pNum" nillable="true" type="decimal" /> 
      </sequence> 
     </complexType> 
     <complexType name="getNewServiceXMLResponse"> 
      <sequence> 
       <element name="result" nillable="true"> 
        <complexType> 
         <sequence> 
          <any /> 
         </sequence> 
        </complexType> 
       </element> 
      </sequence> 
     </complexType> 

CXF生成GetNewServiceXMLResponse类,并在该类中的是结果类。任何字段都在结果中找到。

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "getNewServiceXMLResponse", propOrder = { 
    "result" 
}) 
public class GetNewServiceXMLResponse { 

    @XmlElement(required = true, nillable = true) 
    protected GetNewServiceXMLResponse.Result result; 

... 

    @XmlAccessorType(XmlAccessType.FIELD) 
    @XmlType(name = "", propOrder = { 
     "any" 
    }) 
    public static class Result { 

     @XmlAnyElement(lax = true) 
     protected Object any; 
... 

这是预期的响应格式:

<?xml version="1.0" encoding="UTF-8"?> 
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns0="http://dev.mycompany.com/NewService.wsdl/types/"> 
    <env:Body> 
    <ns0:getNewServiceXMLResponseElement> 
     <ns0:result> 
      <result> 
       <ROWSET> 
        <ROW num="0"> 
         <FIELD1>3649648</FIELD1> 
         <FIELD2>WEEK</FIELD2> 
         <FIELD3>TTN476273</FIELD3> 
        </ROW> 
        <ROW num="1"> 
         <FIELD1>3649650</FIELD1> 
         <FIELD2>WEEK</FIELD2> 
         <FIELD3>TTN476273</FIELD3> 
        </ROW> 
        <ROW num="2"> 
         <FIELD1>540969</FIELD1> 
         <FIELD2>DAY</FIELD2> 
         <FIELD3>null</FIELD3> 
        </ROW> 
       </ROWSET> 
      </result> 
     </ns0:result> 
    </ns0:getNewServiceXMLResponseElement> 
</env:Body> 

我就要挂了ROW NUM = “X” 标记。我以前在SOAP服务中没有见过。所以我试着手工创建ROWSET xml并将其放入一个字符串中。然后通过任何元素传回字符串。为了让它起作用,我将它包装在一个JAXBElement中,以便能够成功解组。

List<Object> dataRecords = manager.getDataRecords(request.getPNum()); 
GetNewServiceXMLResponse response = new GetNewServiceXMLResponse(); 
String str = manager.convertDataToString(dataRecords); 
JAXBElement<String> obj = new JAXBElement<String>(new QName("result"), String.class, str); 
result.setAny(obj); 
response.setResult(result); 

现在这种做法的问题是,字符串被编码的(因为它是一个字符串...)和你最终的&lt; blah blah &gt;等,而不是漂亮的XML。

<getNewServiceXMLResponseElement xmlns="http://dev.mycompany.com/NewService.wsdl/types/"> 
    <result> 
    <result xmlns:ns2="http://dev.mycompany.com/NewService.wsdl/types/" xmlns="">&lt;ROWSET&gt;..snip..&lt;/ROWSET&gt;</result> 
    </result> 
</getNewServiceXMLResponseElement> 

所以下次我试图创建一个文件(通过其他职位的建议)

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
DocumentBuilder builder = factory.newDocumentBuilder(); 
Document document = builder.parse(new InputSource(new StringReader(str))); 
JAXBElement<Document> obj = new JAXBElement<Document>(new QName("result"),Document.class,document); 

这让我的错误:

WARNING: Interceptor has thrown exception, unwinding now 
org.apache.cxf.interceptor.Fault: Marshalling Error: org.w3c.dom.Document is not known to this context 
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.java:159) 
    at org.apache.cxf.jaxb.io.DataWriterImpl.write(DataWriterImpl.java:169) 
    at org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor.writeParts(AbstractOutDatabindingInterceptor.java:105) 
    at org.apache.cxf.interceptor.BareOutInterceptor.handleMessage(BareOutInterceptor.java:68) 
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:236) 
    at org.apache.cxf.interceptor.OutgoingChainInterceptor.handleMessage(OutgoingChainInterceptor.java:74) 
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:236) 
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:104) 
    at org.apache.cxf.transport.servlet.ServletDestination.invoke(ServletDestination.java:99) 
    at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:452) 
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:196) 
    at org.apache.cxf.transport.servlet.AbstractCXFServlet.invoke(AbstractCXFServlet.java:220) 
    at org.apache.cxf.transport.servlet.AbstractCXFServlet.doPost(AbstractCXFServlet.java:153) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:641) 
    at org.apache.cxf.transport.servlet.AbstractCXFServlet.service(AbstractCXFServlet.java:211) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) 
    at com.googlecode.psiprobe.Tomcat70AgentValve.invoke(Tomcat70AgentValve.java:38) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) 
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) 
    at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:200) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585) 
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
    at java.lang.Thread.run(Thread.java:662) 
Caused by: javax.xml.bind.MarshalException 
- with linked exception: 
[javax.xml.bind.JAXBException: org.w3c.dom.Document is not known to this context] 
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:318) 
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:244) 
    at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:75) 
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.writeObject(JAXBEncoderDecoder.java:444) 
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.java:138) 
    ... 31 more 
Caused by: javax.xml.bind.JAXBException: org.w3c.dom.Document is not known to this context 
    at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:246) 
    at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:261) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:144) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:189) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(ElementBeanInfoImpl.java:315) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:322) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:72) 
    at com.sun.xml.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodeProperty.java:111) 
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:332) 
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:699) 
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:152) 
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:332) 
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:699) 
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:152) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:157) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:189) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(ElementBeanInfoImpl.java:315) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:322) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:72) 
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494) 
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:315) 
    ... 35 more 
Caused by: javax.xml.bind.JAXBException: org.w3c.dom.Document is not known to this context 
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:621) 
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:141) 
    ... 53 more 

现在怎么办?我会错误地尝试将XML发送为其它内容(比如字符串)吗?有没有更好地复制ROW num =“X”行为的方法?如果没有能力改变wsdl(以及因此的基础对象),我还没有想出答案。

附加信息:Tomcat的7,Java 1.6的

万一别人有类似的问题,以我的,这里是我想出了一个解决方案。它不漂亮,但它的工作原理。

我创建了,看起来酷似从原来的服务端点的请求映射一个基本的Spring MVC的Web应用程序。在控制器类中,解析输入,从数据库中获取的结果的记录,然后手动地从数据构造的XML字符串:

@RequestMapping(value = "/getNewService", method = RequestMethod.POST) 
public ModelAndView getNewServicePost() { 
    StringWriter writer = new StringWriter(); 
    try { 
     IOUtils.copy(request.getInputStream(), writer, "UTF-8"); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    String theString = writer.toString(); 
    int pNum = XMLUtil.extractNum(theString); 
    List<Object> results = XMLDaoLayer.getResults(pNum); 
    String message = XMLUtil.convertListToResponse(results); 
    return new ModelAndView("response","message",message); 
} 

的响应。JSP页面看起来是这样的:

<%@ page contentType="text/xml" %>${message} 

了SoapUI没有任何麻烦,击中“终点”,并显示结果,它基本上不知道其中的差别。

显然,这不是最佳实践示例。由于多年来服务界面没有发生任何变化,并且在整个过程完全被替换之前不会有任何变化,所以设计实际Web服务的所有好处/优点等都不那么重要。我们只是想让它工作,而且确实如此。