سؤال

After spending a couple of days on trying to find a solution using muenchian grouping I desperately need some help in figuring out the proper xslt code for transforming my XML so that it will produce a unique list of years for all shows (to be imported into Solr eventually). Basically I need the unique grandchildren of a node to be grouped, but somehow all existing solutions on SO did not work for me.

Here's my source XML:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="http://localhost/default.xsl" ?>
<DIRECTORY>
    <SHOWS>
        <SHOW>
            <TITLE>Child's play</TITLE>
            <EVENTS>
                <EVENT>
                    <YEAR>2014</YEAR>
                    <TITLE>Gala day</TITLE>
                </EVENT>
                <EVENT>
                    <YEAR>2014</YEAR>
                    <TITLE>Gala night</TITLE>
                </EVENT>
                <EVENT>
                    <YEAR>2015</YEAR>
                    <TITLE>Gala night</TITLE>
                </EVENT>
            </EVENTS>
        </SHOW>
    </SHOWS>
</DIRECTORY>

Here's the desired output:

<?xml version="1.0"?>
<add>
  <doc>
    <field name="show_title">Child's play</field>
    <field name="show_year">2014</field>
    <field name="show_year">2015</field>
  </doc>
</add>

Here's my XSLT that does not work and is based on various answers around Stack Overflow

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>

    <xsl:key name="show_key"
             match="TITLE"
             use="TITLE"/>

    <xsl:key name="show_years_key"
             match="TITLE"
             use="concat(TITLE, ' ',
                     EVENTS/EVENT/YEAR)"/>

    <xsl:template match="/">
        <add>
            <xsl:for-each select="/DIRECTORY/SHOWS/SHOW">
                <doc>
                    <xsl:call-template name="show_info"/>
                </doc>
            </xsl:for-each>
        </add>
    </xsl:template>

    <xsl:template name="show_info">
        <field name="show_title">
            <xsl:value-of select="TITLE"/>
        </field>
        <xsl:variable
                name="show_events"
                select="key('show_key', TITLE)"/>
        <xsl:for-each
                select="$show_events[generate-id() =
                             generate-id(
                               key('show_years_key',
                                   concat(TITLE,
                                          ' ',
                                          EVENTS/EVENT/YEAR))[1])]">
            <field name="show_years">
                <xsl:value-of select="EVENTS/EVENT/YEAR"/>
            </field>
        </xsl:for-each>

    </xsl:template>
</xsl:stylesheet>

While I really appreciate any help to get to a working solution I could also use some pointers as to why my solution does not work when it should. I left the minimal example a bit more complex on purpose as I suspect my xslt organization might make things more complicated, but the overall solution works quite well, even with multiple different templates.

هل كانت مفيدة؟

المحلول

The grouping key needs to match the elements you're trying to group (i.e. the YEAR elements). I'm having a bit of trouble following your call-template logic, I'd approach it more like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>

    <xsl:key name="show_years_key"
             match="YEAR"
             use="concat(ancestor::SHOW[1]/TITLE, ' ', .)"/>

    <xsl:template match="/">
        <add>
            <xsl:apply-templates select="/DIRECTORY/SHOWS/SHOW" />
        </add>
    </xsl:template>

    <xsl:template match="SHOW">
        <doc>
            <field name="show_title">
                <xsl:value-of select="TITLE"/>
            </field>
            <!-- current() here is the SHOW element this template applies to -->
            <xsl:for-each select="EVENTS/EVENT/YEAR[generate-id() = generate-id(
                    key('show_years_key', concat(current()/TITLE, ' ', .))[1])]">
                <field name="show_years">
                    <xsl:value-of select="." />
                </field>
            </xsl:for-each>
        </doc>
    </xsl:template>
</xsl:stylesheet>

The important point is that you're grouping the YEAR elements by the TITLE of their containing SHOW, not the SHOW elements by the YEAR they contain.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top