问题
I am trying to combine similar records (same UniqueID) with XSL.
Here is my XML:
<ExportXML xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
<record>
<field name="UniqueID">1234</field>
<field name="Location">Michigan</field>
<field name="Category">Math</field>
</record>
<record>
<field name="UniqueID">1234</field>
<field name="Location">Texas</field>
<field name="Category">Science</field>
</record>
<record>
<field name="UniqueID">1234</field>
<field name="Location"></field>
<field name="Category">History</field>
</record>
<record>
<field name="UniqueID">2345</field>
<field name="Location">Ohio</field>
<field name="Category"></field>
</record>
</ExportXML>
Here's what I'd like the output to look like:
<ExportXML>
<record>
<field name="UniqueID">1234</field>
<field name="Location">Michigan, Texas</field>
<field name="Category">Math, Science, History</field>
</record>
<record>
<field name="UniqueID">2345</field>
<field name="Location">Ohio</field>
<field name="Category"></field>
</record>
</ExportXML>
I've tried so many different things that my head is spinning. I'm still new to this, and I'm finding it difficult to learn.
I'm probably way off, but here's what I have so far. To start I was just trying to merge one of the fields (Category) but it just repeats all the records without attempting to merge...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://www.taleo.com/ws/integration/toolkit/2005/07"
exclude-result-prefixes="t">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="distinctRecord" match="t:record" use="t:field[@name='UniqueID']" />
<xsl:template match="/">
<xsl:for-each select="//t:record[generate-id(.) = generate-id(key('distinctRecord', t:field[@name='UniqueID'])[1])]">
<xsl:variable name="UniqueID" select="t:field[@name='UniqueID']" />
<record>
<UniqueID><xsl:value-of select="$UniqueID" /></UniqueID>
<Category>
<xsl:for-each select="key('distinctRecord', $UniqueID)">
<xsl:if test="position() != 1">, </xsl:if>
<xsl:value-of select="t:field[@name='Category']"/>
</xsl:for-each>
</Category>
</record>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Any help would be greatly appreciated!
Thank you.
回答1:
Well your posted input sample is not well-formed, when correcting it I get
<ExportXML>
<record>
<field name="UniqueID">1234</field>
<field name="Location">Michigan</field>
<field name="Category">Math</field>
</record>
<record>
<field name="UniqueID">1234</field>
<field name="Location">Texas</field>
<field name="Category">Science</field>
</record>
<record>
<field name="UniqueID">1234</field>
<field name="Location"></field>
<field name="Category">History</field>
</record>
<record>
<field name="UniqueID">2345</field>
<field name="Location">Ohio</field>
<field name="Category"></field>
</record>
</ExportXML>
then your posted XSLT does use a namespace although the input does not have any, correcting the XSLT I get
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="distinctRecord" match="record" use="field[@name='UniqueID']" />
<xsl:template match="/">
<xsl:for-each select="//record[generate-id(.) = generate-id(key('distinctRecord', field[@name='UniqueID'])[1])]">
<xsl:variable name="UniqueID" select="field[@name='UniqueID']" />
<record>
<UniqueID><xsl:value-of select="$UniqueID" /></UniqueID>
<Category>
<xsl:for-each select="key('distinctRecord', $UniqueID)">
<xsl:if test="position() != 1">, </xsl:if>
<xsl:value-of select="field[@name='Category']"/>
</xsl:for-each>
</Category>
</record>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
then I think you get the grouping you want:
<record>
<UniqueID>1234</UniqueID>
<Category>Math, Science, History</Category>
</record>
<record>
<UniqueID>2345</UniqueID>
<Category/>
</record>
You can then further edit the XSLT to create a root element and to add the location e.g.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="distinctRecord" match="record" use="field[@name='UniqueID']" />
<xsl:template match="/">
<ExportXML>
<xsl:for-each select="//record[generate-id(.) = generate-id(key('distinctRecord', field[@name='UniqueID'])[1])]">
<xsl:variable name="UniqueID" select="field[@name='UniqueID']" />
<record>
<UniqueID><xsl:value-of select="$UniqueID" /></UniqueID>
<Location><xsl:value-of select="field[@name='Location']"/></Location>
<Category>
<xsl:for-each select="key('distinctRecord', $UniqueID)">
<xsl:if test="position() != 1">, </xsl:if>
<xsl:value-of select="field[@name='Category']"/>
</xsl:for-each>
</Category>
</record>
</xsl:for-each>
</ExportXML>
</xsl:template>
</xsl:stylesheet>
So the main grouping in your XSLT is correctly coded, only you need to make sure you apply it to a well-formed XML input.
回答2:
This answer is dependent upon you having access to XSLT 2.0 (Saxon 9.0 or higher) I am using Saxon 9.4.0.4. This allows the use of "for each group".
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://www.taleo.com/ws/integration/toolkit/2005/07"
exclude-result-prefixes="t">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="t:ExportXML">
<ExportXML>
<xsl:for-each-group select="t:record" group-by="t:field[@name='UniqueID']">
<record>
<field name="UniqueID">
<xsl:value-of select="t:field[@name='UniqueID']"></xsl:value-of>
</field>
<field name="Location">
<xsl:for-each select="current-group()">
<xsl:if test="position() != 1 and t:field[@name='Location'] > ' '">,</xsl:if>
<xsl:value-of select="t:field[@name='Location']"/>
</xsl:for-each>
</field>
<field name="Category">
<xsl:for-each select="current-group()">
<xsl:if test="position() != 1 and t:field[@name='Category'] > ' '">,</xsl:if>
<xsl:value-of select="t:field[@name='Category']"/>
</xsl:for-each>
</field>
</record>
</xsl:for-each-group>
</ExportXML>
</xsl:template>
来源:https://stackoverflow.com/questions/14980892/combine-similar-xml-records-fields-with-xsl