2016-01-21 11 views
7

Przed rozpoczęciem: Wiem, że węzeł podrzędny dziedziczy obszar nazw z węzła nadrzędnego i właśnie dlatego występuje mój problem. Niestety serwis internetowy, do którego wysyłam mój XML, nie akceptuje węzła potomnego bez przestrzeni nazw, a ponieważ jest to jednostka rządowa, zmiana ich części jest raczej mało prawdopodobna.Parsowanie pliku XML, który nie zachowuje zduplikowanych przestrzeni nazw w węźle nadrzędnym i podrzędnym węźle

Mając na uwadze powyższe, Używam Spring-WS, aby komunikacja między mojej aplikacji i usługa, więc w taki czy inny ramy wykorzystuje transformator do analizowania mojego ładunku źródło do ramy za ładowności Wynik:

transformer.transform(Source, Result); 

przedtem transformacja nastąpić, mój XML ma te dwa węzły jak wynika tutaj:

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

Po transformacji, drugi nazw jest usuwany (jak już wcześniej powiedziałem, znam powód):

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

Jestem również świadomy, że mogę używać marszałkowców, aby osiągnąć ten sam rezultat i samodzielnie napisać kod parsowania. Korzystanie z tego podejścia jest również ok i byłoby do zaakceptowania, ale nie znam innego sposobu na osiągnięcie tego samego (przekształcenie javax.xml.transform.Source w javax.xml.transform.Result) przy użyciu innych metod poza tym wymienionym powyżej.

Mam dwa pytania wtedy:

1 - Czy mogę uniknąć zachowań mam z podejściem domyślnym (bez użycia marshallers)?

2 - Czy istnieje inne narzędzie, które spowodowałoby tę samą transformację?

+0

czy kontrolujesz wywołanie 'transformer.transform (Source, Result)', tzn. Czy możesz przekazać różne obiekty Source lub Result, jeśli chcesz? – wero

+0

Nie, nie mam nad tym kontroli. Wynik pochodzi z wiosny-ws. –

Odpowiedz

1

Przeżyłem ten sam problem. Niestety (lub nie) WebServiceTemplate z implementacją SOAPMessageFactory (np. SaajSoapMessageFactory) zrobi wszystko, co możliwe, aby upewnić się, że wysyłasz dobrze sformatowany XML jako żądanie, wiążąc Cię z Transformers od Source do Result, w tym nigdy nie pozwól ci powtórzyć "xmlns" "u dzieci, gdy już zrobiłeś to w rodzicu. Masz do wyboru kilka eleganckich opcji - co nie znaczy, że są najprostsze. Możesz pracować na poziomie XML, używając interfejsu javax.xml.ws.Service and Dispatch, co jest całkiem proste, jeśli nie potrzebujesz uwierzytelniania SSL. Sprawdź te linki out (pierwszy z nich został napisany w 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/

Ponadto można spróbować innej fabryki wiadomość, takich jak DomPoxMessageFactory. Ten link może być przydatne:

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

Jeśli jednak zmianę struktury projektu nie jest rozwiązaniem (które było moim przypadku), mam obejście dla Ciebie. Tak, obejście tego problemu, ale gdy usługa docelową spodziewa nieprawidłowy XML, ja odpuszczam sobie: D

Właśnie stworzył abstrakcje HttpComponentsMessageSender i HttpComponentsConnection klas, drugi jest tworzony przez pierwszy czyjegoś sposobu createConnection URI (URI) .Więc mogę tworzyć moje WebServiceTemplate tak:

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

Niestety musisz odpowiadać metodę createConnecion do nowej abstrakcji tylko do wystąpienia połączenia niestandardowego. Jak już powiedziałem, jest to obejście!

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

Wiadomość zostanie wysłana skutecznie wewnątrz onSendAfterWrite metodą (wiadomości WebServiceMessage) klasy HttpComponentsConnection ja abstrahując od. Co zaskakujące, parametr "message" nie jest używany wewnątrz metody. Jest tam tylko dla zasad dziedziczenia. Dobra wiadomość: jest to metoda chroniona. Minusem jest to, że muszę skopiować prawie całą klasę, aby zmienić tylko tę metodę, gdy pola nie mają widoczności publicznej, a struktura będzie potrzebować ich w obsłudze odpowiedzi. Opublikuję całą moją klasę:

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

To jest najprostszy sposób, jaki znalazłem, gdy zmiana struktury projektu nie jest opcją. Mam nadzieję że to pomoże.

+0

Człowieku, uratowałeś mi życie! To się udało. Dzięki! –

0

Nie sądzę, że istnieje jakiekolwiek inne podejście do transformacji. Jak dobrze wiecie, marszałkowie to najlepsza praktyka do zastosowania w tych scenariuszach. Lepiej używać JAXB.

Powiązane problemy