我有以下XML代码段:
<wrapper>
<item timestamp="19.10.2011 12:05">
<comment>Used for orderID '011187' with item 'xyz1'</comment>
</item>
<item timestamp="01.06.2012 16:25">
<comment>Used for orderID '011379' with item 'xyz2'</comment>
</item>
<item timestamp="06.06.2012 14:32">
<comment>Used for orderID '011382' with item 'xyz2'</comment>
</item>
</wrapper>
我想知道每件物品的数量。
在这种情况下:
- 1 x xyz1
- 2 x xyz2
所以你不得不循环遍历所有<item>
,在文本with item...
之后提取引号(&#39;)之间的字符串,然后计算该字符串出现的次数在包装元素中。
您如何在XSLT中解决这个问题?
答案 0 :(得分:2)
在XSLT 2.0中,这有效:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="wrapper">
<xsl:for-each-group select="item/comment"
group-by="replace(tokenize(., ' ')[last()], '[^a-zA-Z0-9]', '')">
<xsl:sort select="current-grouping-key()"/>
<xsl:value-of select="concat('- ', count(current-group()), ' x ', current-grouping-key(), '
')"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:2)
对于XSLT 2,你最好采用fafl的答案。如果您坚持使用XSLT 1,则可以使用以下方法:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="UTF-8" indent="yes" />
<xsl:template match="node()|@*">
<xsl:apply-templates select="node()|@*" />
</xsl:template>
<xsl:template match="comment">
<xsl:variable name="item" select='substring-before(substring-after(., "item '"), "'")' />
<xsl:variable name="quotedItem" select='concat("item '", $item, "'")' />
<xsl:if test='generate-id(.) = generate-id(//comment[contains(text(), $quotedItem)])'>
<xsl:value-of select='count(//comment[contains(text(), $quotedItem)])' />
<xsl:text> x </xsl:text>
<xsl:value-of select="$item" />
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
</xsl:transform>
让我们分解一下。第一个模板非常简单地递归地应用模板。第二个模板匹配所有<comment>
元素。
首先通过获取item '
之后的子字符串然后在该结果中取'
之前的子字符串来提取项目名称。这假设项目名称始终以item 'name'
形式出现。如果没有,你需要调整它。结果将分配给变量item
。请注意,使用单引号会使这有点棘手,因为双引号和单引号是XML标记。因此,select
属性值放在单引号而不是标准双引号之间,实际用作文本的单引号通过'
引用。
然后分配一个名为quotedItem
的变量,该变量基本上是字符串item 'name'
(名称为实际项目值),以便稍后更轻松。它避免在引号之外匹配项目名称或部分匹配(例如,如果一个评论包含item 'xy'
和另一个item 'xyz'
)。同样,这也是对输入的假设。
然后if
元素中的测试检查当前<comment>
的生成ID是否与包含<comment>
的最后quotedItem
的生成ID相同子字符串,以便仅对每个项目的最后一次出现采取操作。在这种情况下,操作是计算包含<comment>
子字符串的所有quotedItem
元素,并将其输出为count x item
,后跟回车符和换行符。
核心部分是变量和generated-id
技巧。其余的将取决于你对结果的意义。
xsltransform link:http://xsltransform.net/a9Giwm
答案 2 :(得分:2)
这是使用Muenchian分组的另一个XSLT 1.0选项(xsl:key
)......
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements= "*"/>
<xsl:key name="items" match="comment"
use="substring-after(normalize-space(),'with item ')"/>
<xsl:template match="/wrapper">
<xsl:for-each select="item/comment[count(.|key('items',
substring-after(normalize-space(),'with item '))[1])=1]">
<xsl:variable name="items" select="key('items',
substring-after(normalize-space(),'with item '))"/>
<xsl:value-of select='concat("- ",count($items)," x ",
translate(substring-after(normalize-space(),"with item "),"'",""),
"
")'/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
示例(因为其他人都在这样做;-): http://xsltransform.net/93dEHFs