У меня была такая же проблема. К сожалению (или нет) WebServiceTemplate с реализацией SOAPMessageFactory (например, SaajSoapMessageFactory) сделает все возможное, чтобы гарантировать, что вы отправляете правильно сформированный XML в качестве запроса, связывая вас с Transformers from Source to Result, в том числе никогда не позволяйте вам повторять «xmlns «у детей, когда вы уже сделали это у родителей. У вас есть несколько элегантных вариантов, чтобы попробовать - что не значит, что они самые простые. Вы можете работать на уровне XML, используя интерфейс javax.xml.ws.Service и Dispatch, что довольно просто, если вам не нужна аутентификация SSL. Проверьте эти ссылки из (первой одна написана в 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/
Также вы можете попробовать другое сообщение фабрику, такие как DomPoxMessageFactory. Эта ссылка может быть полезна:
http://forum.spring.io/forum/spring-projects/web-services/128221-webservicetemplate-get-it-to-stop-adding-soap-envelope
Однако, если изменение структуры вашего проекта не вариант (который был мой случай), у меня есть обходной путь для вас. Да, обходной путь, но как только цель вебсервис IS ОЖИДАЕТ искаженный XML, Я прощаю себя: 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);
}
Сообщение эффективно послан внутри метода onSendAfterWrite (WebServiceMessage сообщение) класса HttpComponentsConnection Я абстрагируясь от. Удивительно, что параметр «сообщение» не используется внутри метода. Он существует только для правил наследования. И хорошие новости: это защищенный метод. Недостатком, опять же, является то, что мне нужно скопировать почти весь класс, чтобы изменить только этот метод, как только поля не будут иметь видимой видимости, и структура будет нужна им при обработке ответов. Таким образом, я выложу весь мой класс вниз:
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();
}
Опять же, это самый простой способ я нашел, когда изменение структуры проекта не вариант. Надеюсь это поможет.
Вы управляете вызовом 'transformer.transform (Source, Result)', т. Е. Можете ли вы передавать разные объекты Source или Result, если хотите? – wero
Нет, у меня нет контроля над этим. Результат исходит из весны-ws. –