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

如何用JAXB在SOAP响应中添加XSD未定义的XML属性?

问题描述

我的服务需要调用下游服务,其SOAP响应包含大量属性,示例如下:

<section name="AA1">
    <grp name="..." AB1001="1.5" AB1002="..." .../>
    <grp name="..." CD1001="1.5" CD1F002="..." .../>
    ...
</section>
<section name="AB1">
    <grp name="..." AB1001="1.5" AB1002="..." .../>
    <grp name="..." CD1001="1.5" CD1F002="..." .../>
    ...
</section>
<section name="AB2">
    ...
</section>
...

我的服务响应需要保留该结构,但由于属性数量庞大,我不想将所有属性添加到自身XSD中,而是希望读取下游服务响应中的属性并将其添加到自身SOAP响应中。

目前我使用JAXB,Java XML Bean由XSD生成,而这些XML属性未在自身XSD中定义,因此需要在JAXB Bean编组为XML(SOAP响应)后手动添加这些属性。

请问是否有可行的实现方式?

我使用Spring和Spring Integration,出站网关配置如下:

@Configuration
public class MppWebServiceGatewayConfig {

    ...
    
    @Bean(name="serviceOutboundGateway")
    public MessageHandler serviceOutboundGateway() {
        final MarshallingWebServiceOutboundGateway gateway = new ExceptionHandlingMarshallingWebServiceOutboundGateway(url, mppMarshaller(), mppUnMarshaller());
        gateway.setMessageSender(httpPoolingWebServiceMessageSender);
        return gateway;
    }

    @Bean
    public Marshaller mppMarshaller() {
        final Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath(CONTEXTPATH);
        return  marshaller;
    }

    @Bean
    public Unmarshaller mppUnMarshaller() {
        final Jaxb2Marshaller unmarshaller = new Jaxb2Marshaller();
        unmarshaller.setContextPath(CONTEXTPATH);
        return  unmarshaller;
    }

    ...
}
可行实现方案

方法一:利用JAXB的@XmlAnyAttribute捕获未知属性

这是最直接的JAXB原生解决方案,无需在编组后手动修改XML。

  1. 修改XSD或生成的Bean
    如果是直接修改生成的Bean,在Section和Grp类中添加注解字段存储未知属性:

    public class Section {
        @XmlAttribute
        private String name;
        
        @XmlElement(name = "grp")
        private List<Grp> grps;
        
        @XmlAnyAttribute
        private Map<QName, String> otherAttributes;
        
        public Map<QName, String> getOtherAttributes() {
            if (otherAttributes == null) {
                otherAttributes = new HashMap<>();
            }
            return otherAttributes;
        }
    }
    
    public class Grp {
        @XmlAttribute
        private String name;
        
        @XmlAnyAttribute
        private Map<QName, String> otherAttributes;
        
        public Map<QName, String> getOtherAttributes() {
            if (otherAttributes == null) {
                otherAttributes = new HashMap<>();
            }
            return otherAttributes;
        }
    }
    

    若通过XSD生成Bean,直接在XSD中添加xs:anyAttribute配置:

    <xs:complexType name="SectionType">
        <xs:sequence>
            <xs:element name="grp" type="GrpType" maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="name" type="xs:string"/>
        <xs:anyAttribute processContents="lax"/>
    </xs:complexType>
    
    <xs:complexType name="GrpType">
        <xs:attribute name="name" type="xs:string"/>
        <xs:anyAttribute processContents="lax"/>
    </xs:complexType>
    

    重新生成Bean后,JAXB会自动将下游响应中的未知属性填充到otherAttributes集合,编组时直接输出这些属性,无需额外处理。

  2. 适配Spring配置
    现有Jaxb2Marshaller配置无需修改,它会自动识别@XmlAnyAttribute注解,完成反序列化捕获与序列化输出。

方法二:在编组后通过DOM操作修改XML

若不想修改XSD或生成的Bean,可在编组完成后通过DOM API手动注入属性。

  1. 自定义Marshaller装饰器
    创建包装类,在编组后处理XML文档:

    public class CustomMarshaller implements Marshaller {
        private final Marshaller delegate;
    
        public CustomMarshaller(Marshaller delegate) {
            this.delegate = delegate;
        }
    
        @Override
        public void marshal(Object graph, Result result) throws JAXBException {
            if (result instanceof DOMResult) {
                delegate.marshal(graph, result);
                Node node = ((DOMResult) result).getNode();
                injectDownstreamAttributes(node);
            } else {
                delegate.marshal(graph, result);
            }
        }
    
        private void injectDownstreamAttributes(Node node) {
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) node;
                if ("section".equals(element.getNodeName())) {
                    Map<QName, String> sectionAttrs = getSectionAttributes(element.getAttribute("name"));
                    sectionAttrs.forEach((qName, value) -> 
                        element.setAttributeNS(qName.getNamespaceURI(), qName.getLocalPart(), value));
                } else if ("grp".equals(element.getNodeName())) {
                    Map<QName, String> grpAttrs = getGrpAttributes(element.getAttribute("name"));
                    grpAttrs.forEach((qName, value) -> 
                        element.setAttributeNS(qName.getNamespaceURI(), qName.getLocalPart(), value));
                }
                NodeList children = element.getChildNodes();
                for (int i = 0; i < children.getLength(); i++) {
                    injectDownstreamAttributes(children.item(i));
                }
            }
        }
    
        // 替换为实际从下游响应获取属性的逻辑
        private Map<QName, String> getSectionAttributes(String sectionName) {
            Map<QName, String> attrs = new HashMap<>();
            attrs.put(new QName("", "customSectionAttr"), "value");
            return attrs;
        }
    
        private Map<QName, String> getGrpAttributes(String grpName) {
            Map<QName, String> attrs = new HashMap<>();
            attrs.put(new QName("", "AB1001"), "1.5");
            attrs.put(new QName("", "CD1001"), "1.5");
            return attrs;
        }
    
        // 其余Marshaller接口方法全部委托给delegate实现
        @Override
        public void marshal(Object graph, Result result, Marshaller.Listener listener) throws JAXBException {
            delegate.marshal(graph, result, listener);
        }
    
        @Override
        public void setProperty(String name, Object value) throws PropertyException {
            delegate.setProperty(name, value);
        }
    
        @Override
        public Object getProperty(String name) throws PropertyException {
            return delegate.getProperty(name);
        }
    
        @Override
        public void setListener(Marshaller.Listener listener) {
            delegate.setListener(listener);
        }
    
        @Override
        public Marshaller.Listener getListener() {
            return delegate.getListener();
        }
    
        @Override
        public void setAdapter(Adapter adapter) {
            delegate.setAdapter(adapter);
        }
    
        @Override
        public <A extends Adapter> void setAdapter(Class<A> type, A adapter) {
            delegate.setAdapter(type, adapter);
        }
    
        @Override
        public <A extends Adapter> A getAdapter(Class<A> type) {
            return delegate.getAdapter(type);
        }
    }
    
  2. 修改Spring配置
    在Marshaller Bean中包装原始Jaxb2Marshaller:

    @Bean
    public Marshaller mppMarshaller() {
        final Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath(CONTEXTPATH);
        return new CustomMarshaller(marshaller);
    }
    

方法三:直接转发下游XML片段(适用于无需修改结构的场景)

若服务响应仅需原样转发下游的section结构,可跳过JAXB序列化/反序列化,直接处理XML payload:

  1. 修改出站网关配置
    使用WebServiceOutboundGateway直接处理XML:

    @Bean(name="serviceOutboundGateway")
    public MessageHandler serviceOutboundGateway() {
        WebServiceOutboundGateway gateway = new WebServiceOutboundGateway(url);
        gateway.setMessageSender(httpPoolingWebServiceMessageSender);
        gateway.setRequestMessageConverter(new SimpleXmlPayloadConverter());
        gateway.setResponseMessageConverter(new SimpleXmlPayloadConverter());
        return gateway;
    }
    
  2. 消息流中拼接XML
    接收到下游XML响应后,通过XSLT或DOM操作提取section节点,直接拼接到服务响应XML中。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:53:13