使用XSLT迭代属性的值

时间:2014-06-11 16:14:53

标签: xml xslt xpath

我有像

这样的XML结构
<Object id="id0000" link="id0010 id0020">
    <Data id="id1000">
      <DataValue name="Obj0000"/>
    </Data>
</Object>

我有几个'Object'节点,其中一些有一个链接,其中一些有两个或多个链接。链接(ID)由空格分隔。我已经使用'tokenize()' - 函数来获取单个链接ID

<xsl:variable name="links">
    <xsl:value-of select="tokenize(@link, ' ')"/>
</xsl:variable>

每个链接指的是带有一些数据的另一个“对象”。

现在我想使用链接(ids)的位置作为字符串,所以我的输出应该看起来像

<Object>
    <hasName>Obj0000</hasName>
    <hasStartLink>id0010</hasStartLink>
</Object>

<LinkedObject>
    <hasID>id0010</hasID>
    <hasDescription>1. link of Obj0000</hasDescription>
    <hasNextLink>id0020</hasNextLink>
</LinkedObject>
<LinkedObject>
    <hasID>id0020</hasID>
    <hasDescription>2. link of Obj0000</hasDescription>
</LinkedObject>

我发现许多页面都说,迭代并保存当前位置是不能以这种方式完成的事情,必须以某种方式解决。我找到了像

这样的东西
<!-- inside another template -->
    <xsl:call-template name="LinkedObj">
        <xsl:with-param name="count" select="1"/>
    </xsl:call-template>
<!-- end of another template -->

<xsl:template name="LinkedObj">
    <xsl:param name="count"/>

    <!-- do some stuff here -->
    <!-- use '$count' as position -->

    <xsl:call-template name="LinkedObj>
        <xsl:with-param name="count" select="$count + 1"/>
    </xsl:call-template>
</xsl:template>

但是现在我不知道我可以使用这样的模板。我宁愿不再在模板本身中调用模板,而是在“另一个模板”中调用模板。但在这个(“另一个”)模板里面,我没有当前的count变量,对吗? 在'LinkedObj'模板中,我已经在另一个上下文中,所以我不知道有多少次我应该调用另一个模板(我没有'链接')。 目前我正在使用两个参数,所以我正在检查我的位置是否小于属性数(链接):

<xsl:template match="Object">
    <xsl:element name="Object>
        <xsl:element name="hasName">
            <xsl:value-of select="Data/DataValue/@name"/>
        </xsl:element>
        <xsl:element name="hasStartLink">
            <!-- here I'm also not sure how to get only the first separated id -->
        </xsl:element>
    </xsl:element>

    <xsl:call-template name="LinkedObj">
        <xsl:with-param name="position" select="1"/>
        <xsl:with-param name="count" select="count(tokenize(@link, ' '))"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="LinkedObj">
    <xsl:param name="position"/>
    <xsl:param name="count"/>

    <!-- do some stuff here -->
    <!-- use '$position' as position -->

    <xsl:if test="$position &lt; $count">
        <xsl:call-template name="LinkedObj>
            <xsl:with-param name="position" select="$position + 1"/>
            <xsl:with-param name="count" select="$count"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

对此有何建议?

下一步不仅要获取位置并将其用于文本输出,还要使用链接的ID来调用或应用新模板。正如我在最后一个代码块中所评论的那样,我不知道如何将标记化字符串的元素用作节点。我用for-each元素尝试了它,但是我没有这样做,因为有一些上下文错误(我必须重现它并在这里发布)。

编辑: 我发现了一种让它工作的方法,这与Tobias的想法不同,所以我在这里发布基本代码:

<xsl:template match="Object">
    <xsl:element name="Object>
        <xsl:element name="hasName">
            <xsl:value-of select="Data/DataValue/@name"/>
        </xsl:element>
        <xsl:element name="hasStartLink">
            <!-- here I'm also not sure how to get only the first separated id -->
        </xsl:element>
    </xsl:element>

    <xsl:call-template name="LinkedObj">
        <xsl:with-param name="position" select="1"/>
        <xsl:with-param name="count" select="count(tokenize(@link, ' '))"/>
        <xsl:with-param name="links" select="tokenize(@link, ' ')"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="LinkedObj">
    <xsl:param name="position"/>
    <xsl:param name="count"/>
    <xsl:param name="links"/>

    <xsl:element name="hasID">
        <xsl:value-of select="$links[$position]"/>
    </xsl:element>

    <xsl:element name="hasDescription">
        <xsl:value-of select="concat($position, '. link of ', Data/DataValue/@name)"/>
    </xsl:element>

    <xsl:if test="$position &lt; $count">
        <xsl:element name="hasNextLink">
            <xsl:value-of select="$links[$position + 1]"/>
        </xsl:element>

        <xsl:call-template name="LinkedObj>
            <xsl:with-param name="position" select="$position + 1"/>
            <xsl:with-param name="count" select="$count"/>
            <xsl:with-param name="links" select="$links"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

在这种情况下,可以在“LinkedObj”模板中获取“Object”的值,因为模板是使用节点调用的(“Object”,因为上下文不会更改)。

1 个答案:

答案 0 :(得分:0)

假设您有多个对象,我在源文件中添加了一个根元素:

<?xml version="1.0" encoding="UTF-8"?>
<Objects>
<Object id="id0000" link="id0010 id0020">
    <Data id="id1000">
        <DataValue name="Obj0000"/>
    </Data>
</Object>
</Objects>

您不必使用LinkedObj模板的递归调用,您可以遍历链接并调用循环:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

<xsl:output indent="yes"/>

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

<xsl:template match="Object">
    <xsl:variable name="name" select="Data/DataValue/@name"/>
    <xsl:variable name="links" select="tokenize(@link, ' ')"/>
    <xsl:element name="Object">
        <xsl:element name="hasName">
            <xsl:value-of select="$name"/>
        </xsl:element>
        <xsl:element name="hasStartLink">
            <xsl:value-of select="$links[1]"/>
        </xsl:element>
    </xsl:element>

    <xsl:for-each select="$links">
        <xsl:variable name="position" select="position()"/>
        <xsl:call-template name="LinkedObj">
            <xsl:with-param name="position" select="$position"/>
            <xsl:with-param name="id" select="."/>
            <xsl:with-param name="nextLink" select="$links[$position + 1]"/>
            <xsl:with-param name="objectName" select="$name"/>
        </xsl:call-template>
    </xsl:for-each>

</xsl:template>

<xsl:template name="LinkedObj">
    <xsl:param name="position"/>
    <xsl:param name="id"/>
    <xsl:param name="nextLink"/>
    <xsl:param name="objectName"/>
    <LinkedObject>
        <hasID>
            <xsl:value-of select="$id"/>
        </hasID>
        <hasDescription>
            <xsl:value-of select="position()"/>
            <xsl:text>. link of </xsl:text>
            <xsl:value-of select="$objectName"/>
        </hasDescription>
        <xsl:if test="$nextLink!=''">
            <hasNextLink>
                <xsl:value-of select="$nextLink"/>
            </hasNextLink>
        </xsl:if>
    </LinkedObject>
</xsl:template>

</xsl:stylesheet>

结果:

<?xml version="1.0" encoding="UTF-8"?>
<Objects>
   <Object>
      <hasName>Obj0000</hasName>
      <hasStartLink>id0010</hasStartLink>
   </Object>
   <LinkedObject>
      <hasID>id0010</hasID>
      <hasDescription>1. link of Obj0000</hasDescription>
      <hasNextLink>id0020</hasNextLink>
   </LinkedObject>
   <LinkedObject>
      <hasID>id0020</hasID>
      <hasDescription>2. link of Obj0000</hasDescription>
   </LinkedObject>
</Objects>

虽然这是必需的输出结构,但不确定这是否正确,我猜测LinkedObjects应该在每个Object中,但是这种改变是微不足道的......