调整JAX-WS序列化DOM的XML命名空间声明风格
调整JAX-WS输出的XML命名空间声明风格
你说得完全对,这两个XML文档在语义上是完全等价的,核心差异只是命名空间的声明和使用风格:文档一采用了带前缀的命名空间绑定(比如ns2、ns3),而文档二则使用了默认命名空间(无前缀)+ 特定元素单独声明命名空间的方式。
下面提供两种可行的方案,帮你把Java应用的JAX-WS输出调整为文档二的风格:
方案一:通过自定义SOAPHandler直接修改消息
JAX-WS允许我们通过SOAPHandler在消息处理流程中直接修改SOAP消息的DOM结构,这是最贴合服务端逻辑的方式:
1. 实现自定义NamespaceHandler
import javax.xml.namespace.QName; import javax.xml.soap.*; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.util.Collections; import java.util.Set; public class NamespaceHandler implements SOAPHandler<SOAPMessageContext> { @Override public boolean handleMessage(SOAPMessageContext context) { try { SOAPMessage message = context.getMessage(); SOAPPart soapPart = message.getSOAPPart(); Document doc = soapPart.getEnvelope().getOwnerDocument(); // 调整Header中的ServerVersionInfo命名空间为默认,并添加xsd/xsi声明 Node serverVersionNode = doc.getElementsByTagNameNS( "http://schemas.microsoft.com/exchange/services/2006/types", "ServerVersionInfo" ).item(0); if (serverVersionNode != null) { Element serverVersionElem = (Element) serverVersionNode; serverVersionElem.setAttribute("xmlns", "http://schemas.microsoft.com/exchange/services/2006/types"); serverVersionElem.setAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); serverVersionElem.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); // 移除原有前缀声明 serverVersionElem.removeAttribute("xmlns:ns2"); } // 调整Body中的GetUserAvailabilityResponse为默认命名空间 Node responseNode = doc.getElementsByTagNameNS( "http://schemas.microsoft.com/exchange/services/2006/messages", "GetUserAvailabilityResponse" ).item(0); if (responseNode != null) { Element responseElem = (Element) responseNode; responseElem.setAttribute("xmlns", "http://schemas.microsoft.com/exchange/services/2006/messages"); responseElem.removeAttribute("xmlns:ns3"); // 为FreeBusyView下的元素单独设置types命名空间 NodeList typeElements = doc.getElementsByTagNameNS( "http://schemas.microsoft.com/exchange/services/2006/types", "*" ); for (int i = 0; i < typeElements.getLength(); i++) { Element elem = (Element) typeElements.item(i); elem.setAttribute("xmlns", "http://schemas.microsoft.com/exchange/services/2006/types"); } } message.saveChanges(); } catch (SOAPException | java.io.IOException e) { e.printStackTrace(); return false; } return true; } @Override public Set<QName> getHeaders() { return Collections.emptySet(); } @Override public boolean handleFault(SOAPMessageContext context) { return true; } @Override public void close(MessageContext context) { // 这里可以做资源清理操作 } }
2. 注册Handler到JAX-WS端点
在发布你的JAX-WS服务时,把上面的Handler添加到端点的处理链中:
import javax.xml.ws.Endpoint; import java.util.Collections; public class ServicePublisher { public static void main(String[] args) { YourServiceImplementation serviceImpl = new YourServiceImplementation(); Endpoint endpoint = Endpoint.create(serviceImpl); // 注册自定义Handler endpoint.getBinding().setHandlerChain(Collections.singletonList(new NamespaceHandler())); endpoint.publish("http://localhost:8080/your-service-path"); System.out.println("Service published successfully"); } }
方案二:使用Transformer+XSLT转换DOM
如果Handler的方式无法满足更复杂的命名空间调整需求,你可以在JAX-WS生成DOM后,用XSLT转换来重构命名空间风格:
1. 编写XSLT转换模板(namespace-transform.xsl)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:ns3="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <xsl:output method="xml" indent="yes" encoding="utf-8"/> <!-- 默认复制所有节点 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 重写ServerVersionInfo的命名空间声明 --> <xsl:template match="ns2:ServerVersionInfo"> <xsl:element name="h:ServerVersionInfo" namespace="http://schemas.microsoft.com/exchange/services/2006/types"> <xsl:attribute name="xmlns" select="'http://schemas.microsoft.com/exchange/services/2006/types'"/> <xsl:attribute name="xmlns:xsd" select="'http://www.w3.org/2001/XMLSchema'"/> <xsl:attribute name="xmlns:xsi" select="'http://www.w3.org/2001/XMLSchema-instance'"/> <xsl:copy-of select="@MajorVersion|@MinorVersion|@MajorBuildNumber|@MinorBuildNumber"/> </xsl:element> </xsl:template> <!-- 将GetUserAvailabilityResponse设为默认命名空间 --> <xsl:template match="ns3:GetUserAvailabilityResponse"> <xsl:element name="GetUserAvailabilityResponse" namespace="http://schemas.microsoft.com/exchange/services/2006/messages"> <xsl:attribute name="xmlns" select="'http://schemas.microsoft.com/exchange/services/2006/messages'"/> <xsl:apply-templates select="@*|node()"/> </xsl:element> </xsl:template> <!-- 为types命名空间下的元素单独添加命名空间声明 --> <xsl:template match="ns2:FreeBusyViewType|ns2:MergedFreeBusy"> <xsl:element name="{local-name()}" namespace="http://schemas.microsoft.com/exchange/services/2006/types"> <xsl:attribute name="xmlns" select="'http://schemas.microsoft.com/exchange/services/2006/types'"/> <xsl:apply-templates select="@*|node()"/> </xsl:element> </xsl:template> <!-- 移除多余的前缀命名空间声明 --> <xsl:template match="@xmlns:ns2|@xmlns:ns3"/> </xsl:stylesheet>
2. Java代码中执行转换
import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; import java.io.File; import java.io.StringWriter; public class XmlTransformer { public String transformNamespaceStyle(Document originalDoc) throws TransformerException { TransformerFactory factory = TransformerFactory.newInstance(); Source xsltSource = new StreamSource(new File("namespace-transform.xsl")); Transformer transformer = factory.newTransformer(xsltSource); DOMSource source = new DOMSource(originalDoc); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); return writer.toString(); } }
两种方案都能实现你的需求,优先推荐方案一,因为它直接集成在JAX-WS的消息处理流程中,不需要额外的DOM转换步骤;如果需要更灵活的XML结构调整,方案二的XSLT方式会更合适。
内容的提问来源于stack exchange,提问作者Thomas Cataldo
相关产品推荐
相关产品推荐

