【问题标题】:Iterate values of an attribute with XSLT使用 XSLT 迭代属性的值
【发布时间】:2014-06-11 16:14:53
【问题描述】:

我有一个类似的 XML 结构

<Object id="id0000" link="id0010 id0020">
    <Data id="id1000">
      <DataValue name="Obj0000"/>
    </Data>
</Object>

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

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

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

现在我想将链接的位置(id)用作字符串,所以我的输出应该类似于

<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>

但现在我不知道我是否可以使用这样的模板。我宁愿不在模板本身而是在“另一个模板”中再次调用模板。但是在这个(“另一个”)模板中,我没有当前的计数变量,对吧? 在“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”,因为上下文不会改变)。

【问题讨论】:

  • 我现在(如上所述)使用“调用模板”而不是“应用模板”,我的上下文没有改变,所以现在我可以在调用模板。在被调用的模板中,我现在可以写&lt;xsl:apply-templates select="//Object[@id=$links[$position]]" mode="Link"/&gt;。该位置将始终获取当前链接,因为它在此模板中递增。
  • 我添加了一个答案,只是作为旁注,不要在变量内使用&lt;xsl:value-of select="tokenize(@link, ' ')"/&gt;,这只会再次给你相同的字符串,要么使用xsl:sequence,要么使用变量的选择属性。
  • 我目前在调用我的“LinkedObj”模板时使用&lt;xsl:with-param name="links" select="tokenize(@link, ' ')"/&gt;。在此模板中,我使用参数“位置”(将递增)来获取标记化字符串的内容,例如$links[$position]。所以也许我没有理解重点:存储在参数“link”中的标记化字符串基本上只是一个字符串(分成一组子字符串)。每次调用“LinkedObj”模板时,我都想访问不同的子字符串。

标签: xml xslt xpath


【解决方案1】:

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

<?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 应该在每个对象内,但是这种更改是微不足道的......

【讨论】:

  • 我尝试使用您的想法,直到我意识到我必须传递每个属性和节点,我想稍后将其作为参数进行处理,因为模板是用字符串而不是节点调用的。我让它按我的方式工作,但现在我想知道是否值得努力改变我的代码来使用你的想法。我认为这与“完成此类任务的常用方法”有关 - 我不知道常用方法。
猜你喜欢
  • 1970-01-01
  • 2013-02-12
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 1970-01-01
  • 2017-06-09
  • 1970-01-01
  • 2019-07-28
相关资源
最近更新 更多