创建表示由2个不同键键入的XML元素位置的元素

时间:2016-05-10 15:11:01

标签: xslt

给出以下XML文档:

<doc>
  <foo>
    <key1>1</key1>
    <key2>1</key2>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>1</key2>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>2</key2>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>2</key2>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>2</key2>
  </foo>
</doc>

我需要将(key1,key2)键入的每组foo元素添加到组中的位置编号,如下所示:

<doc>
  <foo>
    <key1>1</key1>
    <key2>1</key2>
    <pos>1</pos>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>1</key2>
    <pos>2</pos>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>2</key2>
    <pos>1</pos>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>2</key2>
    <pos>2</pos>
  </foo>
  <foo>
    <key1>1</key1>
    <key2>2</key2>
    <pos>3</pos>
  </foo>
</doc>

我尝试创建一个密钥<xsl:key match="foo" use="concat(key1, '|', key2)" name="group"/>,我应该允许按其键值对foo元素进行分组,但我对如何编写相应的模板感到茫然创建pos元素。

模板可能看起来像这样:

<xsl:template match="foo[???]">
  <xsl:copy-of select="."/>
  <xsl:element name="pos">
    <xsl:value-of select="position()" />
  </xsl:element>
</xsl:template>

但是要为&#39; ???&#39;?

提供什么

2 个答案:

答案 0 :(得分:4)

您可以使用

Some-Other-Template.cshtml

完整的样式表

<xsl:template match="foo">
    <xsl:variable name="this" select="."/>
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
        <pos>
            <xsl:for-each select="key('group', concat(key1, '|', key2))">
                <xsl:if test="generate-id() = generate-id($this)">
                    <xsl:value-of select="position()"/>
                </xsl:if>
            </xsl:for-each>
        </pos>
    </xsl:copy>
</xsl:template>

使用XSLT 2.0(以及functx function library的帮助)代码更紧凑:

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

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:key match="foo" use="concat(key1, '|', key2)" name="group"/>

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

    <xsl:template match="foo">
        <xsl:variable name="this" select="."/>
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
            <pos>
                <xsl:for-each select="key('group', concat(key1, '|', key2))">
                    <xsl:if test="generate-id() = generate-id($this)">
                        <xsl:value-of select="position()"/>
                    </xsl:if>
                </xsl:for-each>
            </pos>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

您也可以尝试使用此Muenchian分组版本:

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

    <xsl:key name="k1k2" match="foo" use="concat(key1, '|', key2)"/>

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

    <xsl:template match="/doc">
        <xsl:copy>
            <xsl:for-each
                select="foo[generate-id() = generate-id(key('k1k2', concat(key1, '|', key2))[1])]" >
                <!-- group -->
                <xsl:for-each
                    select="key('k1k2', concat(key1, '|', key2))" >
                    <!-- group member-->
                    <xsl:apply-templates select="."  >
                        <xsl:with-param name="group-pos" select="position()" />
                    </xsl:apply-templates>
                </xsl:for-each>
                </xsl:for-each>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="foo">
        <xsl:param name="group-pos" />
        <xsl:copy>
            <xsl:apply-templates select="*"  />
            <pos>
                <xsl:value-of select="$group-pos"/>
            </pos>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>