如何正确访问XSLT中的祖先元素?

时间:2018-05-17 10:31:18

标签: xml xslt

https://xsltfiddle.liberty-development.net/bFDb2C4

我想将以下xml转换为csv:

<employees>
   <global>
       <attr>test</attr>
   </global>
   <employee>
      <details>
         <name>Joe</name>
         <age>34</age>
         <stage>
            <type code="A" count="1"/>
            <type code="B" count="2"/>
            <type code="C" count="3"/>
         </stage>
      </details>
   </employee>
   <employee>
     <details>
         <age>24</age>
         <name>Sam</name>
      </details>
      <stage>
        <type code="A" count="1"/>
      </stage>
   </employee>
</employees>

结果应该是:

test;Joe;34;A;1
test;Joe;34;B;2
test;Joe;34;C;3
test;Sam;24;A;1

因此我认为我可以匹配最深层次(这里是type),并附加所有ancestor::。以下xslt一般工作,但也输出很多&#34;噪声&#34;围绕期望的结果(参见xsltfidde):

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" omit-xml-declaration="yes" indent="no"/> 
  <xsl:template match="type">
            <xsl:value-of select="ancestor::employees/global/attr,
                    ancestor::employee/details/name,
                    ancestor::employee/details/age,
                    @code,
                    @count"
                separator=";" />
    </xsl:template>
</xsl:stylesheet>

问题:&#34;噪音&#34;来自?我怎样才能收到csv线?

这是目前的结果:

 test



 Joe
 34

    test;Joe;34;A;1
    test;Joe;34;B;2
    test;Joe;34;C;3





 24
 Sam


test;Sam;24;A;1

1 个答案:

答案 0 :(得分:1)

你拥有的模板很好,并且正在完成它的工作。问题是当XSLT开始处理时,它会查找与文档元素/匹配的模板,其模板中没有。当XSLT正在寻找模板且模板中没有匹配的模板时,它会使用built-in templates

基本上,它将遍历XML和输出文本节点。因此,在最终到达匹配type的模板之前,您会获得大量文本输出。

有几种解决方案。一种是使模板匹配/并明确选择type节点

<xsl:template match="/">
  <xsl:apply-templates select="//type" />
</xsl:template>

另一种选择是使模板匹配text()个节点,以忽略它们,从而覆盖默认模板行为

 <xsl:template match="text()" />

但是,当您使用XSLT 3.0时,您也可以执行此操作,而不是添加新模板以指定不采用匹配模板的操作

<xsl:mode on-no-match="shallow-skip"/>

注意,在所有情况下,您都需要在现有模板中输出换行符。

试试这个XSLT

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" omit-xml-declaration="yes" indent="no"/> 

  <xsl:mode on-no-match="shallow-skip"/>

  <xsl:template match="type">
    <xsl:value-of select="ancestor::global/attr,
            ancestor::employee/details/name,
            ancestor::employee/details/age,
            @code,
            @count"
        separator=";" />
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
</xsl:stylesheet>