2016-01-21 49 views
7

在我開始之前:我知道子節點從父節點繼承了命名空間,這就是我的問題發生的原因。不幸的是,我發送XML的web服務不接受沒有命名空間的子節點,因爲它是一個政府實體,它們的改變是不太可能的。解析父母節點和子節點中不保留重複名稱空間的XML

話雖這麼說,我使用Spring-WS,使我的應用程序和Web服務之間的通信,所以在這種或那種方式等框架採用變壓器來我載荷來源解析到框架的有效載荷結果:

transformer.transform(Source, Result); 

之前這種轉變發生的,我的XML具有這兩個節點就像如下位置:

<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10"> 
    <NFe xmlns="http://www.portalfiscal.inf.br/nfe"> 

改造後,第二個命名空間被刪除(正如我之前所說的,我知道的原因):

<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10"> 
    <NFe> 

我也知道我可以使用marshallers來實現相同的結果並自己編寫解析代碼。使用這種方法也可以,並且可以接受,但我不知道使用除上面列出的方法之外的其他方法來實現同樣的任何其他方式(將javax.xml.transform.Source轉換爲javax.xml.transform.Result)。

我有兩個問題,那麼:

1 - 我能避免我用默認的方法有行爲(不使用marshallers)?

2 - 是否有任何其他工具可以進行相同的轉換?

+0

你是否控制調用'transformer.transform(Source,Result)',即如果你想傳遞不同的Source或Result對象? – wero

+0

不,我無法控制。結果來自spring-ws。 –

回答

1

我也經歷過同樣的麻煩。不幸的是,WebServiceTemplate與SOAPMessageFactory的實現(如SaajSoapMessageFactory)將盡一切可能確保您通過將Source綁定到Transformers從Source到Result來發送格式良好的XML作爲請求,包括永不讓你重複'xmlns '如果你已經在父母身上做過這些事情,你有幾個優雅的選擇來嘗試 - 並不意味着它們是最簡單的選項。您可以使用javax.xml.ws.Service和Dispatch接口在XML級別工作,如果您不需要SSL身份驗證,這很容易。檢查這些鏈接了(第一個是寫在鉑-BR)

http://www.guj.com.br/t/nfe-v2-00-veja-como-consumir-o-ws/297304

https://alesaudate.wordpress.com/2010/08/09/how-to-dynamically-select-a-certificate-alias-when-invoking-web-services/

您也可以嘗試其他的消息工廠,如DomPoxMessageFactory。此鏈接可能是有用的:

http://forum.spring.io/forum/spring-projects/web-services/128221-webservicetemplate-get-it-to-stop-adding-soap-envelope

但是,如果更改項目的結構是不是一種選擇(這是我的情況),我對你有一種變通方法。是的,一個解決方法,但一旦目標webservice是預測畸形的XML,我absolve自己:D

我剛剛創建了HttpComponentsMessageSender和HttpComponentsConnection類的抽象,第二個是通過第一個方法createConnection(URI URI) 。所以,我可以創建WebServiceTemplate這樣的:

WebServiceTemplate wst = new WebServiceTemplate(new SaajSoapMessageFactory()); 
wst.setMessageSender(new CustomHttpComponentsMessageSender()); 

可悲的是你需要回復createConnecion方法到新的抽象只是實例化自定義連接。正如我所說,這是一個解決方法!

@Override 
public WebServiceConnection createConnection(URI uri) throws IOException { 
    HttpPost httpPost = new HttpPost(uri); 
    if (isAcceptGzipEncoding()) { 
     httpPost.addHeader(HttpTransportConstants.HEADER_ACCEPT_ENCODING, 
       HttpTransportConstants.CONTENT_ENCODING_GZIP); 
    } 
    HttpContext httpContext = createContext(uri); 
    return new CustomHttpComponentsConnection(getHttpClient(), httpPost, httpContext); 
} 

該消息有效的HttpComponentsConnection類我是從提取的方法onSendAfterWrite(WebServiceMessage消息)內發送。令人驚訝的是,'message'參數不在方法內部使用。它只存在於繼承規則中。好消息是:這是一種受保護的方法。不利的一面是,我需要複製幾乎整個類,以便僅更改此方法,一旦這些字段沒有公開可見性,並且框架將需要它們進行響應處理。所以,我會後我的整個堂課下來:

public class CustomHttpComponentsConnection extends HttpComponentsConnection { 

    private final HttpClient httpClient; 

    private final HttpPost httpPost; 

    private final HttpContext httpContext; 

    private HttpResponse httpResponse; 

    private ByteArrayOutputStream requestBuffer; 

    protected CustomHttpComponentsConnection(HttpClient httpClient, HttpPost httpPost, HttpContext httpContext) { 
     super(httpClient, httpPost, httpContext); 

     Assert.notNull(httpClient, "httpClient must not be null"); 
     Assert.notNull(httpPost, "httpPost must not be null"); 
     this.httpClient = httpClient; 
     this.httpPost = httpPost; 
     this.httpContext = httpContext; 
    } 

    public HttpResponse getHttpResponse() { 
    return httpResponse; 
    } 

    public HttpPost getHttpPost() { 
     return httpPost; 
    } 

    @Override 
    protected OutputStream getRequestOutputStream() throws IOException { 
     return requestBuffer; 
    } 

    @Override 
    protected void onSendBeforeWrite(WebServiceMessage message) throws IOException { 
     requestBuffer = new ByteArrayOutputStream(); 
    } 

    @Override 
    protected void onSendAfterWrite(WebServiceMessage message) throws IOException { 

     OutputStream out = getRequestOutputStream(); 

     String str = out.toString(); 

     str = str.replaceAll("<NFe>", "<NFe xmlns=\"http://www.portalfiscal.inf.br/nfe\">"); 
     ByteArrayOutputStream bs = new ByteArrayOutputStream(); 
     bs.write(str.getBytes()); 

     getHttpPost().setEntity(new ByteArrayEntity(bs.toByteArray())); 

     requestBuffer = null; 
     if (httpContext != null) { 
      httpResponse = httpClient.execute(httpPost, httpContext); 
     } 
     else { 
      httpResponse = httpClient.execute(httpPost); 
     } 
    } 

    @Override 
    protected int getResponseCode() throws IOException { 
     return httpResponse.getStatusLine().getStatusCode(); 
    } 

    @Override 
    protected String getResponseMessage() throws IOException { 
     return httpResponse.getStatusLine().getReasonPhrase(); 
    } 

    @Override 
    protected long getResponseContentLength() throws IOException { 
     HttpEntity entity = httpResponse.getEntity(); 
     if (entity != null) { 
      return entity.getContentLength(); 
     } 
     return 0; 
    } 

    @Override 
    protected InputStream getRawResponseInputStream() throws IOException { 
     HttpEntity entity = httpResponse.getEntity(); 
     if (entity != null) { 
      return entity.getContent(); 
     } 
     throw new IllegalStateException("Response has no enclosing response entity, cannot create input stream"); 
    } 

    @Override 
    public Iterator<String> getResponseHeaderNames() throws IOException { 
     Header[] headers = httpResponse.getAllHeaders(); 
     String[] names = new String[headers.length]; 
     for (int i = 0; i < headers.length; i++) { 
      names[i] = headers[i].getName(); 
     } 
     return Arrays.asList(names).iterator(); 
    } 

    @Override 
    public Iterator<String> getResponseHeaders(String name) throws IOException { 
     Header[] headers = httpResponse.getHeaders(name); 
     String[] values = new String[headers.length]; 
     for (int i = 0; i < headers.length; i++) { 
      values[i] = headers[i].getValue(); 
     } 
     return Arrays.asList(values).iterator(); 
    } 

再次,這是改變項目結構時,我發現最簡單的方法是不是一種選擇。希望這可以幫助。

+0

男人,你救了我的命!這工作得很好。謝謝! –

0

我不認爲那麼你有任何其他的方法來改造。正如你所知道的marshallers是在這些情況下使用的最佳做法。最好使用JAXB。