问题
I have an xml doc that contains comma delimited tags like this...
<?xml version="1.0" encoding="utf-8" ?>
<pages>
<page>
<tags>AAMC 2013, Learning Health System, Cost</tags>
</page>
<page>
<tags>AAMC 2013, Cost, Innovation</tags>
</page>
<page>
<tags>AAMC 2013, Cost, Innovation</tags>
</page>
</pages>
Is it possible, using xslt, to change the xml to display more like the code below where it separates the tag name and counts how many times the tag is referenced?
<?xml version="1.0" encoding="utf-8" ?>
<pages>
<page>
<tag>
<name>AAMC 2013</name>
<amount>3</amount>
</tag>
<tag>
<name>Learning Health System</name>
<amount>1</amount>
</tag>
<tag>
<name>Cost</name>
<amount>3</amount>
</tag>
</page>
<page>
<tag>
<name>AAMC 2013</name>
<amount>3</amount>
</tag>
<tag>
<name>Cost</name>
<amount>3</amount>
</tag>
<tag>
<name>Innovation</name>
<amount>2</amount>
</tag>
</page>
<page>
<tag>
<name>AAMC 2013</name>
<amount>3</amount>
</tag>
<tag>
<name>Cost</name>
<amount>3</amount>
</tag>
<tag>
<name>Innovation</name>
<amount>2</amount>
</tag>
</page>
</pages>
Thanks for any help.
回答1:
IIUC, there are two tasks here:
Tokenize the tags;
Count the occurrences of each tag.
The second task requires the output of the first one as its input - so we will need to do this in two passes:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="sametag" match="token" use="." />
<xsl:template match="/">
<!-- first pass -->
<xsl:variable name="tagnames">
<xsl:for-each select="pages/page">
<page>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="tags" />
</xsl:call-template>
</page>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="tagnames-set" select="exsl:node-set($tagnames)" />
<!-- second (final) pass -->
<pages>
<xsl:for-each select="$tagnames-set/page">
<page>
<xsl:for-each select="token">
<tag>
<name><xsl:value-of select="." /></name>
<amount><xsl:value-of select="count(key('sametag', .))" /></amount>
</tag>
</xsl:for-each>
</page>
</xsl:for-each>
</pages>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="string"/>
<xsl:param name="delimiter" select="', '"/>
<xsl:choose>
<xsl:when test="contains($string, $delimiter)">
<token><xsl:value-of select="substring-before($string, $delimiter)" /></token>
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="substring-after($string, $delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<token><xsl:value-of select="$string"/></token>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Note that this requires the EXSLT node-set() function, which is widely supported by XSLT 1.0 processors. If your processor supports the EXSLT tokenize() function, then you can use it instead of the tokenizing template. The output of that is already a node-set, so that would simplify the stylesheet considerably.
来源:https://stackoverflow.com/questions/21892330/change-format-of-comma-delimited-tags-in-xml-using-xslt