问题
My XML source specifies cross-references in linkGrp
. Based on those
cross-references specified by the @target attribute, I need to group the
values of the s
elements. If the target attribute contains one-to-one
reference, the code I have works well as follows:
<DIV>
<div id="e">
<s id="e1">AAAAA</s>
<s id="e2">BBBBB</s>
<s id="e3">CCCCC</s>
</div>
<div id="fr">
<s id="fr1">DDDDD</s>
<s id="fr2">EEEEE</s>
<s id="fr3">FFFFF</s>
</div>
<linkGrp type="alignment" domains="e fr">
<link target="#e1 #fr1"/>
<link target="#e2 #fr3"/>
<link target="#e3 #fr2"/>
</linkGrp>
</DIV>
The requested HTML output I get is this:
*AAAAA
DDDDD
*BBBBB
FFFFF
*CCCCC
EEEEE
To get this result I use the following XSLT code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="linkId" match="s" use="@id"/>
<xsl:template match="/DIV">
<html>
<head>
<title>Test</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="link">
<ul>
<li>
<xsl:value-of select="key('linkId',substring-before(substring-after(@target, '#'), ' '))"/>
<br/>
<xsl:value-of select="key('linkId',substring-after(@target, ' #'))"/>
</li>
</ul>
</xsl:template>
<xsl:template match="div"/>
<xsl:template match="s"/>
<xsl:template match="*">
<xsl:message terminate="no">
WARNING: Unmatched element: <xsl:value-of select="name()"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
However, my XSLT code does not work that well when I add more cross-references for one @target in the link
elements as this:
<DIV>
<div id="e">
<s id="e1">AAAAA</s>
<s id="e2">BBBBB</s>
<s id="e3">CCCCC</s>
</div>
<div id="fr">
<s id="fr1">DDDDD</s>
<s id="fr2">EEEEE</s>
<s id="fr3">FFFFF</s>
</div>
<linkGrp type="alignment" domains="e fr">
<link target="#e1 #fr1"/>
<link target="#e2 #fr3 #fr1 #fr2"/>
<link target="#e3 #fr2 #fr3"/>
</linkGrp>
</DIV>
With multiple cross-references in the XML source, my XSLT code does not fetch
any match. And I need my code to retrieve all matches specified in the @target
attribute. Can anyone point the mistake in my XSLT code or offer alternative
solution?
回答1:
With xslt-1.0 you may use a recursive template call to split up the ids. Try something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="linkId" match="s" use="@id"/>
<xsl:template match="/DIV">
<html>
<head>
<title>Test</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="link">
<ul>
<li>
<xsl:call-template name="findtarget">
<xsl:with-param name="targets" select="@target" />
</xsl:call-template>
</li>
</ul>
</xsl:template>
<xsl:template name="findtarget">
<xsl:param name="targets" />
<xsl:choose>
<xsl:when test="contains($targets, ' ')">
<xsl:call-template name="findtarget">
<xsl:with-param name="targets" select="substring-before($targets,' ')" />
</xsl:call-template>
<br/>
<xsl:call-template name="findtarget">
<xsl:with-param name="targets" select="substring-after($targets,' ')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="key('linkId',substring-after($targets, '#'))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="div"/>
<xsl:template match="s"/>
<xsl:template match="*">
<xsl:message terminate="no">
WARNING: Unmatched element: <xsl:value-of select="name()"/>
</xsl:message>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output:
WARNING: Unmatched element: linkGrp
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test</title>
</head>
<body>
<ul><li>AAAAA<br>DDDDD</li></ul>
<ul><li>BBBBB<br>FFFFF<br>DDDDD<br>EEEEE</li></ul>
<ul><li>CCCCC<br>EEEEE<br>FFFFF</li></ul>
</body>
</html>
An alternative solution may be to use the xpath function id()
. But to make it work your XML need to have a DTD DOCTYPE.
Something like this:
<!DOCTYPE DIV [
<!ATTLIST s
id ID #REQUIRED
>
]>
<DIV>
<div id="e">
<s id="e1">AAAAA</s>
<s id="e2">BBBBB</s>
<s id="e3">CCCCC</s>
</div>
<div id="fr">
<s id="fr1">DDDDD</s>
<s id="fr2">EEEEE</s>
<s id="fr3">FFFFF</s>
</div>
<linkGrp type="alignment" domains="e fr">
<link target="#e1 #fr1"/>
<link target="#e2 #fr3 #fr1 #fr2"/>
<link target="#e3 #fr2 #fr3"/>
</linkGrp>
</DIV>
With this XML your link template could be change as following:
<xsl:template match="link">
<ul>
<li>
<xsl:for-each select="id(translate(@target,'#', ''))">
<xsl:if test="position() > 1">
<br/>
</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</li>
</ul>
</xsl:template>
来源:https://stackoverflow.com/questions/17579848/how-to-iterate-multiple-cross-references