Xsl:父节点和属性节点的编号

时间:2018-01-03 16:55:02

标签: xml xslt xpath

上一篇文章 - Add attribute based on Node name (incremental) in xsl

感谢Martin,我正在研究基于相同代码的后续项目。在这个项目中,我需要能够增加父节点,以根据原始节点创建一个有点关系的xml输出。我还需要为与当前节点相关的属性创建关系。

预期结果摘要

  • 将所有节点列为 - 已完成
  • a)将所有父节点列为子关系的ID字段 - 完成
  • b)将所有子节点(没有子节点的最低级别节点)列为 - 已完成
  • c)将所有属性列为:
  • i)如果没有子节点存在 - 无法 拉###为父节点
  • ii)如果存在子节点 - 无法为父节点提取###

输入xml:

<xml>
    <individual attr="test" attr2="test3" attr3="test2">
        <name>
            <firstname seq="1">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 seq3="99">Employer1</firstname>
                    <lastname>EmployerLast</lastname>
                </name>
                <addresses>
                    <address>
                        <street>1234 Employer1</street>
                        <city>Smallville</city>
                        <state>CT</state>
                    </address>
                </addresses>
            </employeer>
            <employeer attr="test" attr2="test3" attr3="test2">
                <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>

当前xsl:

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

    <xsl:template match="/*//*[*]">
        <xsl:element name="KEY">
            <xsl:attribute name="name">
                <xsl:value-of select="local-name()"/>_<xsl:number format="001" level="any"/>_ParentID</xsl:attribute>
            <xsl:attribute name="value">
                <xsl:value-of select="name(..)"/>_<xsl:number format="001" level="any"/>
            </xsl:attribute>
        </xsl:element>
        <xsl:for-each select="@*">
            <xsl:element name="KEY">
                <!-- Trying to get the output to list:
                    1) the current node and increment - issue
                    2) the current attribute -->
                <xsl:attribute name="name">
                    <xsl:value-of select="name(..)"/>_<xsl:number format="001" level="any"/>_<xsl:value-of select="name()"/>
                </xsl:attribute>
                <xsl:attribute name="value">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:element>
        </xsl:for-each>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="//*[not(*)]">
        <xsl:element name="KEY">
            <!-- Trying to get the output to list:
                1) the parent node and increment - issue
                2) the current node -->
            <xsl:attribute name="name">
                <xsl:value-of select="name(..)"/>_<xsl:number format="001" level="any"/>_<xsl:value-of select="local-name()"/>
            </xsl:attribute>
            <xsl:attribute name="value">
                <xsl:value-of select="current()"/>
            </xsl:attribute>
        </xsl:element>
        <xsl:for-each select="@*">
            <xsl:element name="KEY">
                <!-- Trying to get the output to list:
                    1) the parent node and increment - issue
                    2) the current node
                    3) the current attribute -->
                <xsl:attribute name="name">
                    <xsl:value-of select="name(../..)"/>_<xsl:number format="001" level="any"/>_<xsl:value-of select="name(..)"/>_<xsl:value-of select="name()"/>
                </xsl:attribute>
                <xsl:attribute name="value">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:element>
        </xsl:for-each>
        <xsl:apply-templates/>
    </xsl:template>

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

代码测试位置 - http://xsltfiddle.liberty-development.net/bFukv89/2

实际与期望结果:

<?xml version="1.0" encoding="utf-16"?>
<KEY name="individual_001_ParentID" value="xml_001" />
<KEY name="individual_001_attr" value="test" />
<KEY name="individual_001_attr2" value="test3" />
<KEY name="individual_001_attr3" value="test2" />
<KEY name="name_001_ParentID" value="individual_001" />
<KEY name="name_001_firstname" value="Me" />
<KEY name="name_001_firstname_seq" value="1" />
<KEY name="name_001_lastname" value="Last" />
<KEY name="addresses_001_ParentID" value="individual_001" />
<KEY name="address_001_ParentID" value="addresses_001" />
<KEY name="address_001_street" value="1234 Main" />
<KEY name="address_001_city" value="Anytown" />
<KEY name="address_001_state" value="TX" />
<KEY name="address_002_ParentID" value="addresses_002" />
<KEY name="address_002_street" value="4321 Central Ave" />
<KEY name="address_002_city" value="Bixby" />
<KEY name="address_002_state" value="ND" />
<KEY name="employeers_001_ParentID" value="individual_001" />
<KEY name="employeer_001_ParentID" value="employeers_001" />
<KEY name="name_002_ParentID" value="employeer_002" />
<KEY name="name_002_firstname" value="Employer1" />
<!-- actual result -->
<KEY name="name_001_firstname_seq3" value="99" />
<!-- desired result as this is the secod occurrance of the parent node-->
<KEY name="name_002_firstname_seq3" value="99" />
<KEY name="name_002_lastname" value="EmployerLast" />
<KEY name="addresses_002_ParentID" value="employeer_002" />
<KEY name="address_003_ParentID" value="addresses_003" />
<KEY name="address_003_street" value="1234 Employer1" />
<KEY name="address_003_city" value="Smallville" />
<KEY name="address_003_state" value="CT" />
<KEY name="employeer_002_ParentID" value="employeers_002" />
<!-- actual result -->
<KEY name="employeer_001_attr" value="test" />
<KEY name="employeer_001_attr2" value="test3" />
<KEY name="employeer_001_attr3" value="test2" />
<!-- desired result as this is the secod occurrance of the parent node-->
<KEY name="employeer_002_attr" value="test" />
<KEY name="employeer_002_attr2" value="test3" />
<KEY name="employeer_002_attr3" value="test2" />
<KEY name="name_003_ParentID" value="employeer_003" />
<KEY name="name_003_firstname" value="Employer2" />
<KEY name="name_003_lastname" value="EmployerLast2" />
<KEY name="addresses_003_ParentID" value="employeer_003" />
<KEY name="address_004_ParentID" value="addresses_004" />
<KEY name="address_004_street" value="1234 Employer2" />
<KEY name="address_004_city" value="Nashville" />
<KEY name="address_004_state" value="TN" />

3 个答案:

答案 0 :(得分:1)

如果我们将编号保存在变量中,例如

<xsl:variable name="first_count">
    <xsl:number format="001" level="any"/>
</xsl:variable>

并使用该变量而不是重复xsl:number

<xsl:value-of select="$first_count"/>

我们可以达到你想要的结果。修改后的样式表如下:

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

    <xsl:template match="/*//*[*]">
        <xsl:variable name="first_count">
            <xsl:number format="001" level="any"/>
        </xsl:variable>
        <xsl:element name="KEY">
            <xsl:attribute name="name">
                <xsl:value-of select="local-name()"/>_<xsl:value-of select="$first_count"/>_ParentID</xsl:attribute>
            <xsl:attribute name="value">
                <xsl:value-of select="name(..)"/>_<xsl:value-of select="$first_count"/>
            </xsl:attribute>
        </xsl:element>
        <xsl:for-each select="@*">
            <xsl:element name="KEY">
                <!-- Trying to get the output to list:
                    1) the current node and increment - issue
                    2) the current attribute -->
                <xsl:attribute name="name">
                    <xsl:value-of select="name(..)"/>_<xsl:value-of select="$first_count"/>_<xsl:value-of select="name()"/>
                </xsl:attribute>
                <xsl:attribute name="value">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:element>
        </xsl:for-each>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="//*[not(*)]">
        <xsl:variable name="second_count">
            <xsl:number format="001" level="any"/>
        </xsl:variable>
        <xsl:element name="KEY">
            <!-- Trying to get the output to list:
                1) the parent node and increment - issue
                2) the current node -->
            <xsl:attribute name="name">
                <xsl:value-of select="name(..)"/>_<xsl:value-of select="$second_count"/>_<xsl:value-of select="local-name()"/>
            </xsl:attribute>
            <xsl:attribute name="value">
                <xsl:value-of select="current()"/>
            </xsl:attribute>
        </xsl:element>
        <xsl:for-each select="@*">
            <xsl:element name="KEY">
                <!-- Trying to get the output to list:
                    1) the parent node and increment - issue
                    2) the current node
                    3) the current attribute -->
                <xsl:attribute name="name">
                    <xsl:value-of select="name(../..)"/>_<xsl:value-of select="$second_count"/>_<xsl:value-of select="name(..)"/>_<xsl:value-of select="name()"/>
                </xsl:attribute>
                <xsl:attribute name="value">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:element>
        </xsl:for-each>
        <xsl:apply-templates/>
    </xsl:template>

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

http://xsltfiddle.liberty-development.net/bFukv89/5中查看它。

答案 1 :(得分:1)

我认为你可以通过捕获变量中的名称/数字并将其作为参数传递给apply-templates来简化样式表。

您还应该能够为没有子元素的所有属性和元素使用单个模板。

似乎没有理由使用xsl:element而不是创建文字结果元素。

我一直使用local-name()。如果需要包含任何名称空间前缀,请改用name()

我也使用过XSLT 2.0,因为它是样式表中指定的版本。在下面的示例链接中,我将引擎更改为Saxon-HE而不是MS XslCompiledTransform(这是一个1.0处理器)。

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/*" priority="1">
    <xsl:apply-templates select="@*|*"/>
  </xsl:template>

  <xsl:template match="*">
    <xsl:param name="parentInfo"/>
    <xsl:variable name="nbr">
      <xsl:number format="000" level="any"/>
    </xsl:variable>
    <xsl:variable name="parentNbr">
      <xsl:number format="000" level="any" select=".."/>
    </xsl:variable>
    <KEY
      name="{string-join((local-name(),$nbr,'ParentID'),'_')}"
      value="{string-join((local-name(..),$parentNbr),'_')}"/>
    <xsl:apply-templates select="@*,*">
      <xsl:with-param name="parentInfo" 
        select="string-join((local-name(),$nbr),'_')"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="@*|*[not(*)]">
    <xsl:param name="parentInfo"/>
    <KEY
      name="{string-join((
      if (self::attribute() and not(../../*[*])) then 
      ($parentInfo,local-name(..)) else $parentInfo,
      local-name()),'_')}"
      value="{.}"
    />
    <xsl:apply-templates select="@*">
      <xsl:with-param name="parentInfo" select="$parentInfo"/>
    </xsl:apply-templates>
  </xsl:template>

</xsl:stylesheet>

<强>输出

<KEY name="individual_001_ParentID" value="xml_001"/>
<KEY name="individual_001_attr" value="test"/>
<KEY name="individual_001_attr2" value="test3"/>
<KEY name="individual_001_attr3" value="test2"/>
<KEY name="name_001_ParentID" value="individual_001"/>
<KEY name="name_001_firstname" value="Me"/>
<KEY name="name_001_firstname_seq" value="1"/>
<KEY name="name_001_lastname" value="Last"/>
<KEY name="addresses_001_ParentID" value="individual_001"/>
<KEY name="address_001_ParentID" value="addresses_001"/>
<KEY name="address_001_street" value="1234 Main"/>
<KEY name="address_001_city" value="Anytown"/>
<KEY name="address_001_state" value="TX"/>
<KEY name="address_002_ParentID" value="addresses_001"/>
<KEY name="address_002_street" value="4321 Central Ave"/>
<KEY name="address_002_city" value="Bixby"/>
<KEY name="address_002_state" value="ND"/>
<KEY name="employeers_001_ParentID" value="individual_001"/>
<KEY name="employeer_001_ParentID" value="employeers_001"/>
<KEY name="name_002_ParentID" value="employeer_001"/>
<KEY name="name_002_firstname" value="Employer1"/>
<KEY name="name_002_firstname_seq3" value="99"/>
<KEY name="name_002_lastname" value="EmployerLast"/>
<KEY name="addresses_002_ParentID" value="employeer_001"/>
<KEY name="address_003_ParentID" value="addresses_002"/>
<KEY name="address_003_street" value="1234 Employer1"/>
<KEY name="address_003_city" value="Smallville"/>
<KEY name="address_003_state" value="CT"/>
<KEY name="employeer_002_ParentID" value="employeers_001"/>
<KEY name="employeer_002_attr" value="test"/>
<KEY name="employeer_002_attr2" value="test3"/>
<KEY name="employeer_002_attr3" value="test2"/>
<KEY name="name_003_ParentID" value="employeer_002"/>
<KEY name="name_003_firstname" value="Employer2"/>
<KEY name="name_003_lastname" value="EmployerLast2"/>
<KEY name="addresses_003_ParentID" value="employeer_002"/>
<KEY name="address_004_ParentID" value="addresses_003"/>
<KEY name="address_004_street" value="1234 Employer2"/>
<KEY name="address_004_city" value="Nashville"/>
<KEY name="address_004_state" value="TN"/>

工作示例:

答案 2 :(得分:0)

至少在XSLT 2或3中,您还可以将编号更改为例如

            <xsl:attribute name="name">
                <xsl:value-of select="name(..)"/>_<xsl:number format="001" select=".." level="any"/>_<xsl:value-of select="name()"/>
            </xsl:attribute>

计算属性的父元素,请参阅https://www.w3.org/TR/xslt20/#number

http://xsltfiddle.liberty-development.net/bFukv89/6