Add attribute based on Node name (incremental) in xsl

时间:2017-12-19 23:10:31

标签: xml xslt

I would like to add an ID attribute to every node that has a child element, based on the node name but increment the reference if one already exist. While I can add the attributes, I am unsure how to increment them based on previous occurrences. Any help in this would be greatly appreciated

Initial xml

<xml>
    <individual>
        <name>
            <firstname>Me</firstname>
            <lastname>Last</lastname>
        </name>
        <addresses>
            <address>
                <street>1234 Main</street>
                <city>Anytown</city>
                <state>TX</state>
            </address>
            <address>
                <street>4321 Central Ave</street>
                <city>Bixby</city>
                <state>ND</state>
            </address>
        </addresses>
        <employeers>
            <employeer>
                <name>
                    <firstname>Employer1</firstname>
                    <lastname>EmployerLast</lastname>
                </name>
                <addresses>
                    <address>
                        <street>1234 Employer1</street>
                        <city>Smallville</city>
                        <state>CT</state>
                    </address>
                </addresses>
            </employeer>
            <employeer>
                <name>
                    <firstname>Employer2</firstname>
                    <lastname>EmployerLast2</lastname>
                </name>
                <addresses>
                    <address>
                        <street>1234 Employer2</street>
                        <city>Nashville</city>
                        <state>TN</state>
                    </address>
                </addresses>
            </employeer>
        </employeers>
    </individual>
</xml>

Desired result

<xml>
    <individual ID="indivdual_1">
        <name ID="name_1">
            <firstname>Me</firstname>
            <lastname>Last</lastname>
        </name>
        <addresses ID="addresses_1">
            <address ID="address_1">
                <street>1234 Main</street>
                <city>Anytown</city>
                <state>TX</state>
            </address>
            <address ID="address_2">
                <street>4321 Central Ave</street>
                <city>Bixby</city>
                <state>ND</state>
            </address>
        </addresses>
        <employeers ID="employeers_1">
            <employeer ID="employeer_1">
                <name ID="name_2">
                    <firstname>Employer1</firstname>
                    <lastname>EmployerLast</lastname>
                </name>
                <addresses ID="addresses_2">
                    <address ID="address_3">
                        <street>1234 Employer1</street>
                        <city>Smallville</city>
                        <state>CT</state>
                    </address>
                </addresses>
            </employeer>
            <employeer ID="employeer_2">
                <name ID="name_3">
                    <firstname>Employer2</firstname>
                    <lastname>EmployerLast2</lastname>
                </name>
                <addresses ID="addresses_3">
                    <address ID="address_4">
                        <street>1234 Employer2</street>
                        <city>Nashville</city>
                        <state>TN</state>
                    </address>
                </addresses>
            </employeer>
        </employeers>
    </individual>
</xml>

I found the following code to count occurrences of a specific element, but am unsure how to apply the count to a specific node.

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text"/>

  <xsl:variable name="newline">
<xsl:text>
</xsl:text>
  </xsl:variable>

  <xsl:key name="elements" match="*" use="name()"/>

  <xsl:template match="/">
    <xsl:value-of select="$newline"/>
    <xsl:text>Summary of Elements</xsl:text>
    <xsl:value-of select="$newline"/>
    <xsl:value-of select="$newline"/>
    <xsl:for-each 
      select="//*[generate-id(.)=generate-id(key('elements',name())[1])]">
      <xsl:sort select="name()"/>
      <xsl:for-each select="key('elements', name())">
        <xsl:if test="position()=1">
          <xsl:text>Element </xsl:text>
          <xsl:value-of select="name()"/>
          <xsl:text> occurs </xsl:text>
          <xsl:value-of select="count(//*[name()=name(current())])"/>
          <xsl:text> times.</xsl:text>
          <xsl:value-of select="$newline"/>
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
    <xsl:text>There are </xsl:text>
    <xsl:value-of select="count(//*)"/>
    <xsl:text> elements in all.</xsl:text>
  </xsl:template>

</xsl:stylesheet>

2 个答案:

答案 0 :(得分:2)

我认为这是xsl:number的工作:

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

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

  <xsl:template match="/*//*[*]">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:attribute name="ID"><xsl:value-of select="local-name()"/>_<xsl:number level="any"/></xsl:attribute>
          <xsl:apply-templates/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

请参阅http://xsltfiddle.liberty-development.net/b4GWV1,它会将您发布的输入转换为输出

<xml>
    <individual ID="individual_1">
        <name ID="name_1">
            <firstname>Me</firstname>
            <lastname>Last</lastname>
        </name>
        <addresses ID="addresses_1">
            <address ID="address_1">
                <street>1234 Main</street>
                <city>Anytown</city>
                <state>TX</state>
            </address>
            <address ID="address_2">
                <street>4321 Central Ave</street>
                <city>Bixby</city>
                <state>ND</state>
            </address>
        </addresses>
        <employeers ID="employeers_1">
            <employeer ID="employeer_1">
                <name ID="name_2">
                    <firstname>Employer1</firstname>
                    <lastname>EmployerLast</lastname>
                </name>
                <addresses ID="addresses_2">
                    <address ID="address_3">
                        <street>1234 Employer1</street>
                        <city>Smallville</city>
                        <state>CT</state>
                    </address>
                </addresses>
            </employeer>
            <employeer ID="employeer_2">
                <name ID="name_3">
                    <firstname>Employer2</firstname>
                    <lastname>EmployerLast2</lastname>
                </name>
                <addresses ID="addresses_3">
                    <address ID="address_4">
                        <street>1234 Employer2</street>
                        <city>Nashville</city>
                        <state>TN</state>
                    </address>
                </addresses>
            </employeer>
        </employeers>
    </individual>
</xml>

答案 1 :(得分:0)

实现这一目标的一种方法是创建具有每个元素的全局计数的替换模板 这意味着给定集合的六个模板:nameindividualaddressaddressesemployeersemployeer

这些模板中的每一个都与//匹配,以获取变量idx中的全局计数。然后,他们用副本替换当前节点并添加新的ID属性。 身份模板复制所有其他节点。

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

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

<!-- global templates for specific element 'name' -->
<xsl:template match="//name">
  <xsl:variable name="idx" select="count(preceding::name) + 1" />
  <xsl:copy>
    <xsl:attribute name="ID"><xsl:value-of select="concat(local-name(),'_',$idx)" /></xsl:attribute>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

<!-- global templates for specific element 'address' -->
<xsl:template match="//address">
  <xsl:variable name="idx" select="count(preceding::address) + 1" />
  <xsl:copy>
    <xsl:attribute name="ID"><xsl:value-of select="concat(local-name(),'_',$idx)" /></xsl:attribute>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

<!-- global templates for specific element 'addresses' -->
<xsl:template match="//addresses">
  <xsl:variable name="idx" select="count(preceding::addresses) + 1" />
  <xsl:copy>
    <xsl:attribute name="ID"><xsl:value-of select="concat(local-name(),'_',$idx)" /></xsl:attribute>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

<!-- global templates for specific element 'individual' -->
<xsl:template match="//individual">
  <xsl:variable name="idx" select="count(preceding::individual) + 1" />
  <xsl:copy>
    <xsl:attribute name="ID"><xsl:value-of select="concat(local-name(),'_',$idx)" /></xsl:attribute>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

<!-- global templates for specific element 'employeers' -->
<xsl:template match="//employeers">
  <xsl:variable name="idx" select="count(preceding::employeers) + 1" />
  <xsl:copy>
    <xsl:attribute name="ID"><xsl:value-of select="concat(local-name(),'_',$idx)" /></xsl:attribute>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

<!-- global templates for specific element 'employeer' -->
<xsl:template match="//employeer">
  <xsl:variable name="idx" select="count(preceding::employeer) + 1" />
  <xsl:copy>
    <xsl:attribute name="ID"><xsl:value-of select="concat(local-name(),'_',$idx)" /></xsl:attribute>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

这是一个XSLT-1.0解决方案,输出应该符合要求。