You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WS端点如何为SOAP Body添加多个子元素

解决Spring WS中SOAP Body包含多个直接子元素的问题

我明白你的痛点——Spring WS默认会把返回对象作为SOAP Body的唯一子元素,导致你不得不额外加一层包装。要实现你想要的Body里同时存在<Response>和<Result>的结构,我们可以通过自定义消息处理逻辑来绕开这个限制,下面给你两种实用的方案:

方案一:自定义PayloadEndpoint直接操作SOAP消息

这种方式能最直接地控制SOAP Body的内容结构,适合需要高度自定义的场景:

  1. 先定义对应XML结构的POJO类(匹配你给出的响应示例):
// ResponseResult.java
public class ResponseResult {
    @XmlAttribute(name = "href")
    private String href;

    // 构造方法、getter、setter省略
}

// Response.java
@XmlRootElement(name = "Response")
public class Response {
    @XmlElement(name = "ResponseResult")
    private ResponseResult responseResult;

    // 构造方法、getter、setter省略
}

// Result.java
@XmlRootElement(name = "Result")
public class Result {
    @XmlAttribute(name = "id")
    private String id;
    // 其他业务字段和getter、setter省略
}
  1. 创建自定义Endpoint,直接操作SOAP Body的DOM结构:
import org.springframework.ws.server.endpoint.PayloadEndpoint;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class MultiElementResponseEndpoint implements PayloadEndpoint {

    @Override
    public Source invoke(Source request) throws Exception {
        // 1. 构建业务数据对象
        Response response = new Response();
        response.setResponseResult(new ResponseResult("#id1"));
        
        Result result = new Result();
        result.setId("id1");
        // 填充Result的其他业务字段
        
        // 2. 获取SOAP Body的DOM文档
        SoapMessage soapMessage = (SoapMessage) org.springframework.ws.context.MessageContextHolder.getRequest().getResponse();
        Document document = ((SaajSoapMessage) soapMessage).getSaajMessage().getSOAPBody().getOwnerDocument();
        
        // 3. 将Response序列化为XML元素并添加到Body
        JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
        Marshaller marshaller = jaxbContext.createMarshaller();
        Element responseElement = document.createElementNS("你的业务命名空间", "Response");
        marshaller.marshal(response, responseElement);
        soapMessage.getSoapBody().addDocumentContent(responseElement);
        
        // 4. 将Result序列化为XML元素并添加到Body
        jaxbContext = JAXBContext.newInstance(Result.class);
        marshaller = jaxbContext.createMarshaller();
        Element resultElement = document.createElementNS("你的业务命名空间", "Result");
        marshaller.marshal(result, resultElement);
        soapMessage.getSoapBody().addDocumentContent(resultElement);
        
        return new DOMSource(document);
    }
}
  1. 在Spring配置中注册这个Endpoint:
<bean id="multiElementEndpoint" class="com.yourpackage.MultiElementResponseEndpoint"/>
<sws:endpoint id="multiElementResponseMapping"
              endpoint-ref="multiElementEndpoint"
              uri="/your-service-path"/>

方案二:自定义MessageConverter适配注解式Endpoint

如果你习惯用@Endpoint注解开发,可以通过自定义消息转换器来支持多元素响应:

  1. 创建自定义消息转换器,处理多对象的序列化:
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.endpoint.adapter.method.MessageConverter;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class MultiElementMessageConverter implements MessageConverter {

    @Override
    public Object fromMessage(WebServiceMessage message, Class<?> targetClass) {
        // 按需实现请求消息的解析逻辑
        return null;
    }

    @Override
    public void toMessage(Object payload, WebServiceMessage message, MessageContext messageContext) throws Exception {
        if (payload instanceof MultiResponseWrapper) {
            MultiResponseWrapper wrapper = (MultiResponseWrapper) payload;
            SoapMessage soapMessage = (SoapMessage) message;
            Document document = ((SaajSoapMessage) soapMessage).getSaajMessage().getSOAPBody().getOwnerDocument();
            
            // 序列化Response并添加到Body
            JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
            Marshaller marshaller = jaxbContext.createMarshaller();
            Element responseElement = document.createElementNS("你的业务命名空间", "Response");
            marshaller.marshal(wrapper.getResponse(), responseElement);
            soapMessage.getSoapBody().addDocumentContent(responseElement);
            
            // 序列化Result并添加到Body
            jaxbContext = JAXBContext.newInstance(Result.class);
            marshaller = jaxbContext.createMarshaller();
            Element resultElement = document.createElementNS("你的业务命名空间", "Result");
            marshaller.marshal(wrapper.getResult(), resultElement);
            soapMessage.getSoapBody().addDocumentContent(resultElement);
        }
    }

    // 包装类,用于传递Response和Result
    public static class MultiResponseWrapper {
        private Response response;
        private Result result;
        // 构造方法、getter、setter省略
    }
}
  1. 在配置类中注册这个转换器:
@Configuration
@EnableWs
public class WebServiceConfig extends WsConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<MessageConverter> messageConverters) {
        messageConverters.add(new MultiElementMessageConverter());
        super.configureMessageConverters(messageConverters);
    }

    // 其他WSDL定义、Endpoint映射等配置省略
}
  1. 在注解式Endpoint中返回包装对象:
@Endpoint
public class YourServiceEndpoint {

    @PayloadRoot(namespace = "你的业务命名空间", localPart = "YourRequest")
    @ResponsePayload
    public MultiElementMessageConverter.MultiResponseWrapper handleRequest(@RequestPayload YourRequest request) {
        Response response = new Response();
        response.setResponseResult(new ResponseResult("#id1"));
        
        Result result = new Result();
        result.setId("id1");
        
        return new MultiElementMessageConverter.MultiResponseWrapper(response, result);
    }
}

关键注意事项

  • 替换代码中的你的业务命名空间为实际的XML命名空间,确保和WSDL定义一致
  • 测试时推荐使用SOAP UI验证返回的消息结构,确认<Response>和<Result>直接作为soap:Body的子元素
  • 如果使用JAXB序列化,确保POJO的注解和XML结构完全匹配,避免出现元素名称、属性不对应的问题

内容的提问来源于stack exchange,提问作者magoeke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:17:09