XSL转换中的命名空间结果错误

时间:2013-10-18 08:17:05

标签: xml xslt namespaces xslt-1.0 xml-namespaces

我遇到了xsl转换的问题。我没有得到我期望的结果,我不知道什么是错的。我认为这个问题与命名空间有关。你能救我吗?

这是我的服务将收到的xml。我想将多值元素分离到一些新节点。

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soap-env:Header/>
   <soap-env:Body>
      <testService facade="Test" xmlns="http://new.webservice.namespace">
         <input>
            <Data1>Data 1</Data1>
            <Data2>Data 2</Data2>
            <ParamResponses>
               <ParamResponse>
                  <Name>DATAONE</Name>
                  <ValParam>Text 1</ValParam>
               </ParamResponse>
               <ParamResponse>
                  <Name>DATATWO</Name>
                  <ValParam>Text 2</ValParam>
               </ParamResponse>
               <ParamResponse>
                  <Name>MULTIVALUED</Name>
                  <ValParam>001</ValParam>
                  <ValParam>002</ValParam>
               </ParamResponse>
               <ParamResponse>
                  <Name>DATATHREE</Name>
                  <ValParam>Text 3</ValParam>
               </ParamResponse>
            </ParamResponses>
         </input>
      </testService>
   </soap-env:Body>
</soap-env:Envelope>

这是我正在申请的xslt

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@facade">
        <xsl:attribute name="facade">
               <xsl:text>FacadeReplaced</xsl:text>
            </xsl:attribute>
    </xsl:template>
   <xsl:template match="input/ParamResponses">
      <ParamResponses>
         <xsl:for-each select="ParamResponse[Name!='MULTIVALUED']">
            <ParamResponse>
               <xsl:copy>
         <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
            </ParamResponse>
         </xsl:for-each>
      </ParamResponses>
      <MultiValueParamResponses>
         <MultiValueParamResponse>
            <Name>MULTIVALUED</Name>
           <xsl:variable name="items" select="//input/ParamResponses/ParamResponse[Name='MULTIVALUED']/ValParam"/>
            <ValueList>
               <xsl:for-each select="$items">
                  <value>
                     <xsl:value-of select="."/>
                  </value>
               </xsl:for-each>
            </ValueList>
         </MultiValueParamResponse>
      </MultiValueParamResponses>
   </xsl:template>
   <xsl:template match="@* | node()">
      <xsl:copy>
         <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

这是我获得的:

<?xml version="1.0" encoding="UTF-16"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soap-env:Header/>
    <soap-env:Body>
        <testService facade="FacadeReplaced" xmlns="http://new.webservice.namespace">
            <input>
                <Data1>Data 1</Data1>
                <Data2>Data 2</Data2>
                <ParamResponses>
                    <ParamResponse>
                        <Name>DATAONE</Name>
                        <ValParam>Text 1</ValParam>
                    </ParamResponse>
                    <ParamResponse>
                        <Name>DATATWO</Name>
                        <ValParam>Text 2</ValParam>
                    </ParamResponse>
                    <ParamResponse>
                        <Name>MULTIVALUED</Name>
                        <ValParam>001</ValParam>
                        <ValParam>002</ValParam>
                    </ParamResponse>
                    <ParamResponse>
                        <Name>DATATHREE</Name>
                        <ValParam>Text 3</ValParam>
                    </ParamResponse>
                </ParamResponses>
            </input>
        </testService>
    </soap-env:Body>
</soap-env:Envelope>

但这是我想要获得的:

<?xml version="1.0" encoding="UTF-16"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soap-env:Header/>
    <soap-env:Body>
        <testService facade="FacadeReplaced" xmlns="http://new.webservice.namespace">
            <input>
                <Data1>Data 1</Data1>
                <Data2>Data 2</Data2>
                <ParamResponses>
                    <ParamResponse>
                        <ParamResponse>
                            <Name>DATAONE</Name>
                            <ValParam>Text 1</ValParam>
                        </ParamResponse>
                    </ParamResponse>
                    <ParamResponse>
                        <ParamResponse>
                            <Name>DATATWO</Name>
                            <ValParam>Text 2</ValParam>
                        </ParamResponse>
                    </ParamResponse>
                    <ParamResponse>
                        <ParamResponse>
                            <Name>DATATHREE</Name>
                            <ValParam>Text 3</ValParam>
                        </ParamResponse>
                    </ParamResponse>
                </ParamResponses>
                <MultiValueParamResponses>
                    <MultiValueParamResponse>
                        <Name>MULTIVALUED</Name>
                        <ValueList>
                            <value>001</value>
                            <value>002</value>
                        </ValueList>
                    </MultiValueParamResponse>
                </MultiValueParamResponses>
            </input>
        </testService>
    </soap-env:Body>
</soap-env:Envelope>

如果我在元素输入中添加了一个xmlns,我已经正确地获得了它,但这不是我将通过webservice获得的。

转型应该是XSLT 1.0。

2 个答案:

答案 0 :(得分:2)

问题确实与命名空间有关。在原始XML中,您有这一行

 <testService facade="Test" xmlns="http://new.webservice.namespace">

这意味着 testService 元素,它下面的所有元素都将成为“http://new.webservice.namespace”的一部分(除非被其他命名空间声明覆盖)。

但是,在您的XSLT中,没有提到此命名空间。这意味着当你有这样的表达式时......

<xsl:template match="input/ParamResponses">

正在寻找属于NO命名空间的元素。由于源XML在命名空间中包含元素,因此该模板不匹配任何内容。在您的情况下,身份模板将匹配,导致您的输出与输入相同。

因此,您需要做的是在XSLT中声明命名空间

<xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ns="http://new.webservice.namespace">

然后,无论何时引用输入XML中的元素,都必须使用相关的命名空间前缀

<xsl:template match="ns:input/ns:ParamResponses">

注意名称空间前缀“ns”实际上可以是任何东西。 URI“http://new.webservice.namespace”必须与XML中的URI匹配。

此外,对于您输出的任何新元素,如果您希望这些元素成为命名空间的一部分,您可以在它们前面加上命名空间前缀,或者您可以在XSLT中声明一个默认命名空间,这将适用于任何新的您输出的元素没有前缀

试试这个XSLT

<xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ns="http://new.webservice.namespace"
     xmlns="http://new.webservice.namespace">

   <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@facade">
        <xsl:attribute name="facade">
               <xsl:text>FacadeReplaced</xsl:text>
            </xsl:attribute>
    </xsl:template>
   <xsl:template match="ns:input/ns:ParamResponses">
      <ParamResponses>
         <xsl:for-each select="ns:ParamResponse[ns:Name!='MULTIVALUED']">
            <ParamResponse>
               <xsl:copy>
         <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
            </ParamResponse>
         </xsl:for-each>
      </ParamResponses>
      <MultiValueParamResponses>
         <MultiValueParamResponse>
            <Name>MULTIVALUED</Name>
           <xsl:variable name="items" select="//ns:input/ns:ParamResponses/ns:ParamResponse[ns:Name='MULTIVALUED']/ns:ValParam"/>
            <ValueList>
               <xsl:for-each select="$items">
                  <value>
                     <xsl:value-of select="."/>
                  </value>
               </xsl:for-each>
            </ValueList>
         </MultiValueParamResponse>
      </MultiValueParamResponses>
   </xsl:template>
   <xsl:template match="@* | node()">
      <xsl:copy>
         <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:0)

这个XSLT样式表:

<!-- Define a 'web' namespace that is equal to the default namespace in your input.
     This way, we can match elements in your input (like ParamResponse, Name) 
     that belong to this namespace. 
     We also need to define the same namespace as the default, so that we can
     exclude it from elements we output. -->
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:web="http://new.webservice.namespace"
                xmlns="http://new.webservice.namespace"
                exclude-result-prefixes="web #default">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- The identity transform: outputs identical XML to the input, in the
       absence of template overrides. -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@facade">
    <xsl:attribute name="facade">
      <xsl:text>FacadeReplaced</xsl:text>
    </xsl:attribute>
  </xsl:template>

  <!-- Match ParamResponse elements in the web namespace. -->
  <xsl:template match="web:ParamResponses">
    <!-- Output the single-valued ParamResponse elements first. These will
         match the identity transform and just get copied out. -->
    <xsl:apply-templates select="web:ParamResponse[not(web:Name='MULTIVALUED')]"/>
    <!-- Output a new MultiValueParamResponses element and apply-templates to
         all ParamResponse elements with a MULTIVALUED Name element. -->
    <MultiValueParamResponses>
      <xsl:apply-templates select="web:ParamResponse[web:Name='MULTIVALUED']"/>
    </MultiValueParamResponses>
  </xsl:template>

  <!-- For the MULTIVALUED ParamResponse elements, change the element name
       and start a new ValueList element. -->
  <xsl:template match="web:ParamResponse[web:Name='MULTIVALUED']">
    <MultiValueParamResponse>
      <xsl:apply-templates select="web:Name" />
      <ValueList>
        <xsl:apply-templates select="web:ValParam" mode="multi"/>
      </ValueList>
    </MultiValueParamResponse>
  </xsl:template>

  <!-- Change the name of the ValParam elements for MULTIVALUED
       ParamResponse elements. -->
  <xsl:template match="web:ValParam" mode="multi">
    <value>
      <xsl:value-of select="."/>
    </value>
  </xsl:template>
</xsl:stylesheet>

在应用于您的输入时生成以下输出XML:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soap-env:Header />
  <soap-env:Body>
    <testService facade="FacadeReplaced" xmlns="http://new.webservice.namespace">
      <input>
        <Data1>Data 1</Data1>
        <Data2>Data 2</Data2>
        <ParamResponse>
          <Name>DATAONE</Name>
          <ValParam>Text 1</ValParam>
        </ParamResponse>
        <ParamResponse>
          <Name>DATATWO</Name>
          <ValParam>Text 2</ValParam>
        </ParamResponse>
        <ParamResponse>
          <Name>DATATHREE</Name>
          <ValParam>Text 3</ValParam>
        </ParamResponse>
        <MultiValueParamResponses>
          <MultiValueParamResponse>
            <Name>MULTIVALUED</Name>
            <ValueList>
              <value>001</value>
              <value>002</value>
            </ValueList>
          </MultiValueParamResponse>
        </MultiValueParamResponses>
      </input>
    </testService>
  </soap-env:Body>
</soap-env:Envelope>