XSLT如何遍历每个节点并将其包装在标签中

时间:2017-05-26 22:43:15

标签: xml xslt

这是XML:

<svg xmlns:xlink="http://www.w3.org/1999/xlink">
    <g>
        <text id="b376">
            <tspan x="59" y="156" font-size="13px" font-family="Arial">80</tspan>
        </text>
        <use xlink:href="#b376" fill="#000000"/>
        <text id="b374">
            <tspan x="59" y="204" font-size="13px" font-family="Arial">60</tspan>
        </text>
        <use xlink:href="#b374" fill="#000000"/>
        <defs>testDef</defs>
     </g>
</svg>

这是我的XSL输入:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="g">
    <g>
      <xsl:apply-templates select="use|defs"/>
      <defs>
        <xsl:apply-templates select="*[name() != 'use' and name() != 'defs']"/>
      </defs>
    </g>
  </xsl:template>
</xsl:stylesheet>

我想将所有节点包装在defs标签中除了使用标签和defs标签。因此,2个文本节点将包含在defs标签中,但defs和use不会。

这是我得到的

<?xml version="1.0"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
  <g>
    <use xlink:href="#b376" fill="#000000"/>
    <use xlink:href="#b374" fill="#000000"/>
    <defs>testDef</defs>
    <defs>
      <text id="b376">
        <tspan x="59" y="156" font-size="13px" font-family="Arial">80</tspan>
      </text>
      <text id="b374">
        <tspan x="59" y="204" font-size="13px" font-family="Arial">60</tspan>
      </text>
    </defs>
  </g>
</svg>

这就是我想要的:

<?xml version="1.0"?>
    <svg xmlns:xlink="http://www.w3.org/1999/xlink">
      <g>
        <use xlink:href="#b376" fill="#000000"/>
        <use xlink:href="#b374" fill="#000000"/>
        <defs>testDef</defs>
        <defs>
          <text id="b376">
            <tspan x="59" y="156" font-size="13px" font-family="Arial">80</tspan>
          </text>
        </defs>
         <defs>
          <text id="b374">
            <tspan x="59" y="204" font-size="13px" font-family="Arial">60</tspan>
          </text>
        </defs>
      </g>
    </svg>

我正在使用@TessellatingHeckler's comment在线工具进行测试。谢谢!

2 个答案:

答案 0 :(得分:1)

您的当前输出(其中所有<text>元素都包含在单个<defs>标记中)正是我对读取您的XSL代码所期望的 - 对于每个<g>,您有一个<defs> ,其中您处理的所有元素都不是<use><defs>

<xsl:template match="g">
    <g>
        <xsl:apply-templates select="use|defs"/>

        <!-- This part here: -->
        <defs>
            <xsl:apply-templates select="*[name() != 'use' and name() != 'defs']"/>
        </defs>

    </g>
</xsl:template>

由于<xsl:apply-templates select="*[name() != 'use' and name() != 'defs']"/> ,所以非<defs>和非{{1}的文字use全部在单个文字refs元素中,元素作为批处理。

您显然希望每个非<defs>和非use元素都包含在自己的defs中。在这种情况下,您需要将文字<defs>移动到 中与非<defs>和非use元素匹配的单独模板。

快速而肮脏的重构可能如下所示:

defs

请注意,此方法还会将原始<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <!-- Identity template --> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <!-- We don't really need a specific template for <g>, so we let the identity template handle that case. --> <!-- The only case where we need to define a different flow is for children of <g> elements, that aren't <use> or <defs>. --> <xsl:template match="*[name(..) = 'g'][name() != 'use' and name() != 'defs']"> <!-- The template matches _each_ such element, so if we put the literal `<defs>` here, we get that `<defs>` as a wrapper for _each_ such element. --> <defs> <xsl:copy-of select="."/> </defs> </xsl:template> </xsl:stylesheet> <use>元素保留在<defs>父级中的相同位置。

答案 1 :(得分:1)

您的预期输出显示元素的顺序 对你很重要。

您需要第一个 usedefs代码,并且只需 所有剩余的元素,每个元素都包含在自己的defs中 元件。

为了实现这一点,请使用以下脚本:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="g">
    <g>
      <xsl:apply-templates select="use|defs"/>
      <xsl:for-each select="*[name() != 'use' and name() != 'defs']">
        <defs>
          <xsl:apply-templates select="."/>
        </defs>
      </xsl:for-each>
    </g>
  </xsl:template>

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

如您所见,我添加了for-each循环select 从您的示例复制的属性。

此循环的内容:

  • 创建包装器defs元素
  • 在其中&#34;重播&#34;源元素 (循环的当前元素)。