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.
czy kontrolujesz wywołanie 'transformer.transform (Source, Result)', tzn. Czy możesz przekazać różne obiekty Source lub Result, jeśli chcesz? – wero
Nie, nie mam nad tym kontroli. Wynik pochodzi z wiosny-ws. –