使用XSLT计算不同的项并解析逗号分隔的值

时间:2010-06-23 20:50:26

标签: xslt parsing

假设我有这样的XML:

<child_metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem3]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1, SampleItem2]"/>
        </attributes>
    </metadata>
</child_metadata>

我想要做的是计算metadata_valuelists中不同值的数量。有以下不同的值:SampleItem1,SampleItem2和SampleItem3。所以,我希望得到一个值3.(尽管SampleItem1出现两次,我只计算一次。)

如何在XSLT中执行此操作?

我意识到这里存在两个问题:第一,分隔列表中逗号分隔的值,第二,计算唯一值的数量。但是,我不确定我能否将这两个问题的解决方案结合起来,这就是为什么我把它作为一个问题。

3 个答案:

答案 0 :(得分:3)

没有扩展的另一种方式:

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

    <xsl:variable name="all-value" select="/*/*/*/*/@value"/> 

    <xsl:template match="/"> 
        <xsl:variable name="count"> 
            <xsl:apply-templates select="$all-value"/> 
        </xsl:variable> 
        <xsl:value-of select="string-length($count)"/> 
    </xsl:template>  

    <xsl:template match="@value" name="value">
        <xsl:param name="meta" select="translate(.,'[] ','')"/>
        <xsl:choose>
            <xsl:when test="contains($meta,',')">
                <xsl:call-template name="value">
                    <xsl:with-param name="meta" select="substring-before($meta,',')"/>
                </xsl:call-template>
                <xsl:call-template name="value">
                    <xsl:with-param name="meta" select="substring-after($meta,',')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:if test="count(.|$all-value[contains(translate(.,'[] ','&#xA;&#xA;&#xA;'),
                                                          concat('&#xA;',$meta,'&#xA;'))][1])=1">
                    <xsl:value-of select="1"/> 
                </xsl:if> 
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template> 

</xsl:stylesheet> 

注意:也许可以使用xsl:key代替xsl:variable进行优化 修改:匹配棘手的元数据。

答案 1 :(得分:1)

这(注意:只是一个)转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 >
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kValue" match="value" use="."/>

 <xsl:template match="/">
   <xsl:variable name="vRTFPass1">
    <values>
     <xsl:apply-templates/>
    </values>
   </xsl:variable>

   <xsl:variable name="vPass1"
        select="msxsl:node-set($vRTFPass1)"/>

   <xsl:for-each select="$vPass1">
     <xsl:value-of select=
      "count(*/value[generate-id()
                    =
                     generate-id(key('kValue', .)[1])
                    ]
             )
      "/>
   </xsl:for-each>
 </xsl:template>

 <xsl:template match="metadata_valuelist">
  <xsl:call-template name="tokenize">
    <xsl:with-param name="pText" select="translate(@value, '[],', '')"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="tokenize">
    <xsl:param name="pText" />

    <xsl:choose>
      <xsl:when test="not(contains($pText, ' '))">
        <value><xsl:value-of select="$pText"/></value>
      </xsl:when>
      <xsl:otherwise>
        <value>
         <xsl:value-of select="substring-before($pText, ' ')"/>
        </value>
        <xsl:call-template name="tokenize">
         <xsl:with-param name="pText" select=
          "substring-after($pText, ' ')"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<child_metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem3]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1]"/>
        </attributes>
    </metadata>
    <metadata>
        <attributes>
            <metadata_valuelist value="[SampleItem1, SampleItem2]"/>
        </attributes>
    </metadata>
</child_metadata>

生成想要的正确结果

3

请注意:因为这是一个XSLT 1.0解决方案,所以必须将第一次传递的结果从臭名昭着的RTF类型转换为常规树。这是使用您的XSLT 1.0处理器的xxx:node-set()函数完成的 - 在我的例子中,我使用了msxsl:node-set()。

答案 2 :(得分:-1)

您可能想要分两个阶段考虑这个问题;首先,做一个分解这些值属性的转换,然后计算它们是相当简单的。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="@value">
    <xsl:call-template name="breakdown">
      <xsl:with-param name="itemlist" select="substring-before(substring-after(.,'['),']')" />
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="breakdown">
    <xsl:param name="itemlist" />
    <xsl:choose>
      <xsl:when test="contains($itemlist,',')">
        <xsl:element name="value">
          <xsl:value-of select="normalize-space(substring-before($itemlist,','))" />
        </xsl:element>
        <xsl:call-template name="breakdown">
          <xsl:with-param name="itemlist" select="substring-after($itemlist,',')" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="value">
          <xsl:value-of select="normalize-space($itemlist)" />
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

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

除了底部的'catch all'模板之外,它会以您给出的格式选取任何值属性,并将它们分解为单独的元素(作为'metadata_valuelist'元素的子元素),如下所示:< / p>

...
<metadata_valuelist>
  <value>SampleItem1</value>
  <value>SampleItem2</value>
</metadata_valuelist>
...

'substring-before / substring-after选择你看到'['和']'之后的顶部条带附近,然后将它传递给'细分'模板。这个模板将检查它的'itemlist'参数中是否有逗号,如果有,它会在它之前将文本作为'value'元素的内容吐出,然后用列表的其余部分递归调用自身。如果参数中没有逗号,它只是将参数的整个内容输出为“值”元素。

然后运行:

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

  <xsl:key name="itemvalue" match="value" use="text()" />

  <xsl:template match="/">
    <xsl:value-of select="count(//value[generate-id(.) = generate-id(key('itemvalue',.)[1])])" />
  </xsl:template>
</xsl:stylesheet>

关于从第一个转换获得的XML,它只会将一个值作为文本输出吐出,告诉您有多少个不同的值。

编辑:我应该指出,这个解决方案对你的输入做了一些假设:

  • 文档中的其他任何位置都没有名为“value”的属性;如果有,你可以修改@value匹配,专门挑选出这些。
  • 文档中没有任何名称为'value'的元素;当第一个转换创建它们时,第二个转换将无法区分它们。如果有,您可以使用尚未使用的元素名称替换两个<xsl:element name="value">行。
  • @value属性的内容始终以'['开头,以']'结尾,列表中没有']'字符;如果有的话,'substring-before'函数将丢弃第一个']'之后的所有内容,而不仅仅是结尾处的']'。
  • 您要计算的项目名称中没有逗号,例如[SampleItem1,“Sample2,3”]。如果有,'“Sample2”和“3”将被视为单独的项目。