2

I have the following XML coming from a SharePoint control. I would like to transform using XSLT to produce a nested ul>li list. But I am having problem as I iterate each row, the primary folder repeats for each row, rather than making one primary Folder node and adding values of Menu_Display_name underneath that node to mimic a treeview... My XML is like:

<dsQueryResponse>
    <NewDataSet>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ CORP HR TIME SELF SERVICE" SECONDARY_FOLDER="Time" MENU_DISPLAY_NAME="Create Timecard"></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ CORP HR TIME SELF SERVICE" SECONDARY_FOLDER="Time" MENU_DISPLAY_NAME="Recent Timecards"></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ CORP HR TIME SELF SERVICE" SECONDARY_FOLDER="Time" MENU_DISPLAY_NAME="Templates"></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ CORP HR TIME SELF SERVICE" SECONDARY_FOLDER="Time" MENU_DISPLAY_NAME="Timecard Search"></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ CORP EXP ENTRY" SECONDARY_FOLDER="" MENU_DISPLAY_NAME="Expenses Home"></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ HR EMP SELF SERVICE" SECONDARY_FOLDER="" MENU_DISPLAY_NAME="Accommodation Request"></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ HR EMP SELF SERVICE" SECONDARY_FOLDER="" MENU_DISPLAY_NAME="Additional Personal Information" ></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ HR EMP SELF SERVICE" SECONDARY_FOLDER="" MENU_DISPLAY_NAME="All Actions Awaiting Your Attention"></Row>
        <Row DOMAIN_USERNAME="someUser" PRIMARY_FOLDER="XYZ HR EMP SELF SERVICE" SECONDARY_FOLDER="" MENU_DISPLAY_NAME="Appraisals"></Row>
    </NewDataSet>
</dsQueryResponse>

my "Poor man's attempt" at XSL is like this:

 <xsl:template name="dvt_1.body">
    <xsl:param name="Rows" />
    <xsl:param name="FirstRow" />
    <xsl:param name="LastRow" />
    <ul>
      <xsl:for-each select="$Rows">
        <xsl:if test="position() &gt;= $FirstRow and position() &lt;= $LastRow">
          <xsl:call-template name="dvt_1.rowview" />
        </xsl:if>
      </xsl:for-each>
    </ul>
  </xsl:template>


  <xsl:template name="dvt_1.rowview">

    <li class="isFolder isExpanded">
       <xsl:value-of select="@PRIMARY_FOLDER" />
       <xsl:choose>
         <xsl:when test="@SECONDARY_FOLDER != ''">
           <ul>
             <li class="isFolder isExpanded">
               <xsl:value-of select="@SECONDARY_FOLDER" />
               <ul>
                  <li><xsl:value-of select="@MENU_DISPLAY_NAME" /></li>
               </ul>
             </li>
           </ul>
         </xsl:when>
         <xsl:otherwise>
           <ul>
              <li><xsl:value-of select="@MENU_DISPLAY_NAME" /></li>
           </ul>
          </xsl:otherwise>
        </xsl:choose>
    </li>

  </xsl:template>

And my desired xsl output is like this:

<div id="navigator">
        <ul>
            <li class="isFolder isExpanded">
               XYZ CORP HR TIME SELF SERVICE    
                <ul>
                    <li class="isFolder isExpanded">
                        Time
                            <ul>
                                <li><a href="#" target="_tab">Create Timecard</a></li>
                                <li><a href="#" target="_tab">Recent Timecards</a></li>
                                <li><a href="#" target="_tab">Templates</a></li>
                                <li><a href="#" target="_tab">Timecard Search</a></li>                                   
                            </ul>
                    </li>
                </ul>        
            </li>
            <li class="isFolder isExpanded">
                XYZ CORP EXP ENTRY
                <ul>
                    <li><a href="#" target="_tab">Expense Home</a></li>      
                </ul>
            </li> 
            <li class="isFolder isExpanded">
                XYZ HR EMP SELF SERVICE
                <ul>
                    <li><a href="#" target="_tab">Accommodation Request</a></li>
                    <li><a href="#" target="_tab">Additional Personal Information</a></li>
                    <li><a href="#" target="_tab">All Actions Awaiting Your Attention</a></li>
                    <li><a href="#" target="_tab">Appraisals</a></li>
                </ul>
            </li>
        </ul>
    </div>

Can someone please help me achieve this using xslt?

4
  • and can you show us your xslt code ? Commented Mar 10, 2016 at 16:05
  • @JEY Sure updated. Commented Mar 10, 2016 at 16:06
  • is Muenchian grouping what I should be looking at? Can someone please help me out? Commented Mar 10, 2016 at 16:32
  • indeed it's your best chance or if you are using xslt2 you can use for-each-group Commented Mar 10, 2016 at 16:35

1 Answer 1

4

Muenchian grouping is indeed what you need to be looking at in XSLT 1.0 (which is what Sharepoint uses I believe). First you grouping by the PRIMARY_VALUE attribute, so you have a key like this:

<xsl:key name="primary" match="Row" use="@PRIMARY_FOLDER" />

But I will assume you can possibly have multiple SECONDARY_FOLDER for a given PRIMARY_VALUE, so you would need a second key:

<xsl:key name="secondary" match="Row" use="concat(@PRIMARY_FOLDER, '|', @SECONDARY_FOLDER)" />

You start off by selecting the rows with the first occurrence of each PRIMARY_FOLDER value

<xsl:for-each select="$rows[generate-id() = generate-id(key('primary', @PRIMARY_FOLDER)[1])]">

And within this, you then select the rows with the distinct SECONDARY_VALUE to form the basis of your nested lists

<xsl:apply-templates select="key('primary', @PRIMARY_FOLDER)[generate-id() = generate-id(key('secondary', concat(@PRIMARY_FOLDER, '|', @SECONDARY_FOLDER))[1])]" mode="secondary" />

The only extra work is that you have slightly different behaviour based on whether the SECONDARY_FOLDER is populated or not. You can do this with two separate templates though.

Try this XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html" indent="yes" />

    <xsl:key name="primary" match="Row" use="@PRIMARY_FOLDER" />
    <xsl:key name="secondary" match="Row" use="concat(@PRIMARY_FOLDER, '|', @SECONDARY_FOLDER)" />

    <xsl:variable name="rows" select="//Row" />

    <xsl:template match="/">
      <ul>
          <xsl:for-each select="$rows[generate-id() = generate-id(key('primary', @PRIMARY_FOLDER)[1])]">
              <li class="isFolder isExpanded">
                  <xsl:value-of select="@PRIMARY_FOLDER" />
                  <xsl:apply-templates select="key('primary', @PRIMARY_FOLDER)[generate-id() = generate-id(key('secondary', concat(@PRIMARY_FOLDER, '|', @SECONDARY_FOLDER))[1])]" mode="secondary" />
              </li>
          </xsl:for-each>
      </ul>
    </xsl:template>

    <xsl:template match="Row[@SECONDARY_FOLDER != '']" mode="secondary">
        <li class="isFolder isExpanded">
            <xsl:value-of select="@SECONDARY_FOLDER" />
            <ul>
                <xsl:apply-templates select="key('secondary', concat(@PRIMARY_FOLDER, '|', @SECONDARY_FOLDER))" />
            </ul>
        </li>
    </xsl:template>

    <xsl:template match="Row" mode="secondary">
        <xsl:apply-templates select="key('primary', @PRIMARY_FOLDER)" />
    </xsl:template>

    <xsl:template match="Row">
        <li>
            <a href="#" target="_tab">
                <xsl:value-of select="@MENU_DISPLAY_NAME" />
            </a>
        </li>
    </xsl:template>
</xsl:stylesheet>

See this in action at http://xsltransform.net/pPzifq9.

Note, if your actual XML has namespaces, you would need to modify the XSLT to take these into account.

Sign up to request clarification or add additional context in comments.

1 Comment

SharePoint uses the .NET-integrated XSLT implementation, so it's limited to XSLT 1.0.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.