XSLT Problem Using Muenchian Method of Grouping
I'm having a very odd problem with grouping a set of nodes in a document and getting a unique list based on a set of contained values.
Given the following XML:
<Person>
<FirstName>John</FirstName>
<MiddleName>Q</MiddleName>
<LastName>Person</LastName>
<Gift>
<Thing>Moon</Thing>
<Friend>
<FirstName>Billy</FirstName>
<MiddleName>Bob</MiddleName>
<LastName>Smith</LastName>
</Friend>
<Friend>
<FirstName>Mary</FirstName>
<MiddleName>Jo</MiddleName>
<LastName>Smith</LastName>
</Friend>
</Gift>
<Gift>
<Thing>Pencil</Thing>
开发者_运维问答 </Gift>
</Person>
I am trying to generate a pipe-delimited string with all of the names in the "Friend" tags. Here is the current stylesheet I'm using:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="Friends" match="Friend" use="concat(FirstName, MiddleName, LastName)"/>
<xsl:template match="/Person">
<xsl:for-each select="Gift/Friend[count(. | key('Friends', concat(FirstName, MiddleName, LastName))[1]) = 1]">
<xsl:apply-templates select="."/>
<xsl:if test="position()!=last()">
<xsl:text>|</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="Friend">
<xsl:variable name="name">
<xsl:value-of select="FirstName"/><xsl:text> </xsl:text>
<xsl:value-of select="MiddleName"/><xsl:text> </xsl:text>
<xsl:value-of select="LastName"/><xsl:text> </xsl:text>
</xsl:variable>
<xsl:value-of select="normalize-space($name)"/>
</xsl:template>
</xsl:stylesheet>
The problem I'm having is that the output omits the last friend. Here is the result:
Billy Bob Smith|
Funny thing is that if I remove the last "Gift" element, it produces the output I'm looking for:
Billy Bob Smith|Mary Jo Smith
For reference, here is the Java code used to execute the transform:
package testing;
import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class test
{
public static void main( String[] args )
{
try
{
StreamSource xsl = new StreamSource( "xslt/person.xslt" );
StreamSource xml = new StreamSource( "xslt/person.xml" );
Transformer txfm = TransformerFactory.newInstance().newTransformer(xsl);
if ( txfm != null )
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
txfm.transform( xml, new StreamResult(bytes) );
System.out.println( bytes.toString() );
}
}
catch ( Exception e )
{
e.printStackTrace();
}
}
}
Any assistance you can offer would be welcome.
Your transform tested with Saxon 6.5 is working perfectly and it produces the wanted result:
Billy Bob Smith|Mary Jo Smith
It might be an issue related to the java XSLT api. May be the API does not fully support XSLT 1.0 features like, for instance, xsl:key
. You can make a further test and try not using keys at all.
For example, this template performs the same task without using any key:
<xsl:template match="/Person">
<xsl:for-each select="Gift/Friend[not(
concat(FirstName,MiddleName,LastName)
=
preceding::Friend[concat(FirstName, MiddleName, LastName)]
)]">
<xsl:apply-templates select="."/>
<xsl:if test="position()!=last()">
<xsl:text>|</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
精彩评论