2016-01-21 31 views
7

Prima di iniziare: So che il nodo figlio eredita lo spazio dei nomi dal nodo padre ed è per questo che si verifica il problema. Sfortunatamente, il servizio web che sto inviando il mio XML non accetta il nodo figlio senza lo spazio dei nomi e, dato che è un'entità governativa, un cambiamento nella loro parte è piuttosto improbabile.Analisi di un XML che non mantiene gli spazi dei nomi duplicati nel nodo padre e nel nodo figlio

Detto questo, sto usando Primavera-WS per rendere la comunicazione tra la mia domanda e il webservice, quindi in un modo o l'altro il quadro utilizza un trasformatore per analizzare il mio carico Source per payload Risultato del quadro:

transformer.transform(Source, Result); 

prima che la trasformazione abbia luogo, il mio XML ha questi due nodi come segue qui:

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

Dopo la trasformazione, il secondo spazio dei nomi viene rimosso (come ho detto prima, io so il motivo):

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

Sono anche consapevole che posso utilizzare i marshaller per ottenere lo stesso risultato e scrivere il codice di analisi personalmente. Usare questo approccio è ok e sarebbe accettabile, ma non conosco altro modo per ottenere la stessa cosa (trasformando lo javax.xml.transform.Source in javax.xml.transform.Result) usando un altro approccio oltre a quello sopra elencato.

ho due domande allora:

1 - Posso evitare il comportamento che sto avendo con l'approccio di default (senza l'utilizzo di marshaller)?

2 - C'è qualche altro strumento che farebbe la stessa trasformazione?

+0

controlli la chiamata 'transformer.transform (Source, Result)', cioè puoi passare diversi oggetti Source o Result se vuoi? – wero

+0

No, non ne ho il controllo. Il risultato arriva da Spring-ws. –

risposta

1

Ho avuto lo stesso problema. Sfortunatamente (o meno) WebServiceTemplate con l'implementazione di SOAPMessageFactory (come SaajSoapMessageFactory) farà tutto il possibile per assicurare che tu stia inviando un XML ben formato come richiesta legandoti ai Transformer da Source a Result, incluso non lasciare mai ripetere 'xmlns' 'nei bambini quando hai già fatto in genitore. Hai un paio di opzioni eleganti da provare - ciò che non significa che siano le più semplici. Puoi lavorare a livello XML usando l'interfaccia javax.xml.ws.Service e Dispatch, che è abbastanza facile se non hai bisogno dell'autenticazione SSL. Controllare questi collegamenti out (primo è scritto in Pt-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/

Inoltre si può provare un altro stabilimento messaggio, come ad esempio DomPoxMessageFactory. Questo collegamento può essere utile:

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

Tuttavia, se cambiando la struttura del progetto non è un'opzione (che era il mio caso), ho una soluzione per voi. Sì, una soluzione, ma una volta che il webservice bersaglio si aspetta un XML valido, mi assolve me: D

Ho appena creato astrazioni di HttpComponentsMessageSender e HttpComponentsConnection classi, la seconda viene creata un'istanza attraverso il metodo del primo uno CreateConnection (Uri Uri) .Così posso creare il mio WebServiceTemplate in questo modo:

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

Purtroppo dovrete rispondere il metodo createConnecion alla nuova astrazione solo per un'istanza di connessione personalizzata. Come ho detto, è una soluzione!

@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); 
} 

il messaggio viene inviato in modo efficace all'interno del metodo onSendAfterWrite (messaggio WebServiceMessage) della classe HttpComponentsConnection sto astraendo da. Sorprendentemente, il parametro 'message' non è usato all'interno del metodo. È lì solo per le regole di ereditarietà. E la buona notizia: è un metodo protetto. Il rovescio della medaglia, ancora, è che ho bisogno di copiare quasi l'intera classe per poter cambiare solo questo metodo, una volta che i campi non hanno visibilità pubblica, e il framework ne avrà bisogno nella gestione della risposta. Quindi, vi posterò la mia intera classe verso il basso:

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(); 
    } 

Ancora una volta, questo è il modo più semplice che ho trovato quando si cambia la struttura del progetto non è un'opzione. Spero che questo ti aiuti.

+0

Uomo, mi hai salvato la vita! Ha funzionato bene. Grazie! –

0

Non penso che ci sia un altro approccio per la vostra trasformazione. Come sapete, i marshaller sono la migliore pratica da utilizzare in questi scenari. Meglio usare JAXB.