2016-01-21 16 views
7

Bevor ich beginne: Ich weiß, dass der Kindknoten den Namespace vom Elternknoten erbt und deshalb tritt mein Problem auf. Leider akzeptiert der Webdienst, den ich meine XML sende, den Kindknoten nicht ohne den Namensraum, und da es sich um eine Regierungseinheit handelt, ist eine Änderung in ihrem Teil eher unwahrscheinlich.Parsen eines XML-Dokuments, das doppelte Namespaces im übergeordneten Knoten und untergeordneten Knoten nicht enthält

aber sagen, dass ich mit Spring-WS, die Kommunikation zwischen meiner Anwendung zu machen und den Webservice, so in der einen oder der anderen der Rahmen einen Transformator verwendet meine Nutzlast Quelle zum Rahmennutzlasten Ergebnis zu analysieren:

transformer.transform(Source, Result); 

Davor Transformation stattfindet, hat meine XML diese beiden Knoten, wie es hier folgt:

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

Nach der Transformation wird der zweite Namensraum entfernt (wie ich schon sagte, ich kenne den Grund):

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

Mir ist auch bewusst, dass ich Marshaller verwenden kann, um das gleiche Ergebnis zu erzielen und den Parsecode selbst zu schreiben. Die Verwendung dieses Ansatzes ist ebenfalls in Ordnung und wäre akzeptabel, aber ich kenne keinen anderen Weg, um das gleiche zu erreichen (Umwandlung des javax.xml.transform.Source in javax.xml.transform.Result), wobei ein anderer Ansatz neben dem oben aufgeführten verwendet wird.

Ich habe zwei Fragen dann:

1 - Kann ich vermeiden das Verhalten, das ich mit dem Standard-Ansatz habe (ohne Einweiser zu verwenden)?

2 - Gibt es ein anderes Werkzeug, das die gleiche Umwandlung machen würde?

+0

steuern Sie den Aufruf 'transformer.transform (Source, Result)', d. H. Können Sie verschiedene Source- oder Result-Objekte übergeben, wenn Sie möchten? – wero

+0

Nein, ich habe keine Kontrolle darüber. Das Ergebnis kommt aus dem Frühjahr. –

Antwort

1

Ich habe die gleichen Probleme durchgemacht. Leider (oder nicht) WebServiceTemplate mit der Implementierung von SOAPMessageFactory (wie SaajSoapMessageFactory) wird alles tun, um sicherzustellen, dass Sie ein wohlgeformtes XML als Anfrage senden, indem Sie an die Transformer von Quelle zu Ergebnis gebunden werden, einschließlich "xmlns" 'bei Kindern, wenn Sie bereits in Elternteil getan haben. Sie haben ein paar elegante Optionen zu versuchen - was nicht heißt, dass sie die einfachsten sind. Sie können auf XML-Ebene arbeiten, indem Sie die Schnittstellen javax.xml.ws.Service und Dispatch verwenden, was sehr einfach ist, wenn Sie keine SSL-Authentifizierung benötigen. Schauen Sie sich diese Links aus (erste ist geschrieben 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/

Sie können auch eine andere Nachricht Fabrik versuchen, wie DomPoxMessageFactory. Dieser Link könnte nützlich sein:

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

Wenn jedoch die Struktur Ihres Projektes zu ändern, ist keine Option (was mein Fall war), ich habe eine Lösung für Sie. Ja, ein Problem zu umgehen, aber sobald das Ziel Webservice ein ungültiges XML erwartet, ich entbindet mich: D

ich gerade erstellt Abstraktionen von HttpComponentsMessageSender und HttpComponentsConnection Klassen, die zweite ist durch die erste Methode Create (URI uri) instanziiert .So kann ich meine WebServiceTemplate wie folgt erstellen:

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

Leider müssen Sie die createConnecion Methode auf die neue Abstraktion antworten nur die benutzerdefinierte Verbindung instanziieren. Wie gesagt, es ist ein Workaround!

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

Die Nachricht wird aus dem Inneren der Methode onSendAfterWrite (WebServiceMessage Nachricht) der HttpComponentsConnection Klasse ich abstrahieren effektiv gesendet. Überraschenderweise wird der Parameter 'message' nicht innerhalb der Methode verwendet. Es ist nur für Vererbungsregeln da. Und die gute Nachricht: Es ist eine geschützte Methode. Der Nachteil ist wiederum, dass ich fast die gesamte Klasse kopieren muss, um nur diese Methode zu ändern, sobald die Felder keine öffentliche Sichtbarkeit haben und das Framework sie in der Antwortbehandlung benötigt. Also, ich werde meine ganze Klasse schreiben unten:

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

Auch dies der einfachste Weg ist, fand ich, wenn Projektstruktur zu ändern, ist keine Option. Hoffe das hilft.

+0

Mann, du hast mein Leben gerettet! Das hat gut funktioniert. Vielen Dank! –

0

Ich denke nicht, dann gibt es einen anderen Ansatz für Ihre Transformation. Wie Sie wissen, sind Marshaller in diesen Szenarien die beste Vorgehensweise. Besser JAXB zu verwenden.