如何使用XSLT修改XML Soap响应?

时间:2012-05-16 12:05:10

标签: xml xslt soap

我有一个相对简单的Soap响应消息XML,我想用XSLT修改它。

这是我到目前为止所拥有的:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:z="http://some.url/WS/">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="@* | node()">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
    </xsl:template>

    <xsl:variable name="Method" select="name(//soap:Body/*)" />

    <xsl:template match="//soap:Body/*" >
      <xsl:choose>
        <xsl:when test="$Method='Method1Response'">
          <xsl:call-template name="Method1" />
        </xsl:when>
        <xsl:otherwise>

        </xsl:otherwise>
      </xsl:choose>      
    </xsl:template>

      <xsl:template name="Method1" match="//soap:Body/z:Method1Response/z:Method1Result/z:Errors" />

</xsl:stylesheet>

示例XML:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<env:Header xmlns:env="http://www.w3.org/2003/05/soap-envelope">
  <wsa:Action></wsa:Action>
  <wsa:MessageID></wsa:MessageID>
  <wsa:RelatesTo></wsa:RelatesTo>
  <wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
</env:Header>
  <soap:Body>
    <Method1Response xmlns="http://some.url/WS/">
      <Method1Result>
        <Msg>blah blah</Msg>
        <Errors>
          <ErrorItem>error 1</ErrorItem>
          <ErrorItem>error 2</ErrorItem>
        </Errors>
        <Data />
      </Method1Result>
    </Method1Response>
  </soap:Body>
</soap:Envelope>

如果Method1 =特定值,那么想法是从Method1Result下删除错误。如果没有,请保持原样。我目前拥有的,它没有做到这一点。感谢。

进一步澄清的另一个编辑: 我想为一个属于不同Web服务调用的多个XML文件提供一个XSLT文件。这意味着Method1可以有许多不同的值,例如:GetMeal,GetWindow,GetHandle,GetWeatherReport,...我希望为同一个XSLT中的每个值创建一个“case”,以便故意“打破”用于测试目的的预期XML。每次我都会删除不同的元素。

2 个答案:

答案 0 :(得分:0)

问题是匹配 //soap:Body/* 的模板调用命名模板,该模板本身不输出任何内容。这会导致忽略 soap:Body 下的所有元素。

由于您已经有一个模板可以匹配您要删除的元素,因此您可以删除与 soap:Body 相匹配的模板,您会发现它将按预期工作。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" 
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
    xmlns:z="http://some.url/WS/"> 
    <xsl:output method="xml" indent="yes"/> 

    <xsl:template match="@* | node()"> 
      <xsl:copy> 
        <xsl:apply-templates select="@* | node()"/> 
      </xsl:copy> 
    </xsl:template> 

    <xsl:template match="//soap:Body/z:Method1Response/z:Method1Result/z:Errors" /> 
</xsl:stylesheet> 

当这应用于您的示例XML时,以下是输出

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
   <env:Header xmlns:env="http://www.w3.org/2003/05/soap-envelope">
      <wsa:Action/>
      <wsa:MessageID/>
      <wsa:RelatesTo/>
      <wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
   </env:Header>
   <soap:Body>
      <Method1Response xmlns="http://some.url/WS/">
         <Method1Result>
            <Msg>blah blah</Msg>
            <Data/>
         </Method1Result>
      </Method1Response>
   </soap:Body>
</soap:Envelope>

编辑:没有理由不能使用其他匹配模板来处理您可能会收到的其他方法响应。例如....

<xsl:template match="//soap:Body/z:GetMealResponse/z:GetMealResult/z:Leftovers" />
<xsl:template match="//soap:Body/z:Method1Response/z:Method1Result/z:Errors" /> 

答案 1 :(得分:0)

从样式表示例:

<xsl:variable name="Method" select="name(//soap:Body/*)" />

<xsl:template match="//soap:Body/*" >
  <xsl:choose>
    <xsl:when test="$Method='Method1Response'">
      <xsl:call-template name="Method1" />

摆脱变量$Method,你不需要它。

注意匹配模式(如在match="//soap:Body/*"中,这是尴尬的),与*搜索表达式(如在select="name(//soap:Body/*)"中)相比,通常可以相当简单并且不要需要搜索整个doc运算符(所以你简化为match="soap:Body/*")。

不是使用显式条件逻辑,而是提供一个与您的特殊情况相匹配的模板,并确保它具有比其他候选模板更高的优先级,以便匹配:

<xsl:template match="z:Method1Response"/><!-- suppressed -->

或明确提升优先级:

<xsl:template match="z:Method1Response" priority="2"/>

或更具体地说是包含错误:

<xsl:template match="z:Method1Response[z:Method1Result/z:Errors]"/>