xsl-list
[Top] [All Lists]

Identity Transform Grouping Question

2004-10-13 08:44:46
   I have an identity transform that attempts to group elements at a deeply 
nested level, but I am having trouble accomplishing this.  The xslt performs 
the identity transform and sorts the result tree on a few levels, all 
successfully.  The problem occurs when I attempt the deep-level grouping.  

Given the following source tree sample:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="offices.xsl" ?>
<locations version="1.0">
   <divisions>
      <division id="B">
         <regions>
            <region name="europe">
               <countries>
                  <country name="England">
                     <offices>
                        <office id="BR4">
                           <names>
                              <name lang='en'>Branch 4</name>
                           </names>
                           <address>
                              <line>26 Abbey Lane</line>
                              <line>London</line>
                           </address>
                           <cities>
                              <city>London</city>
                           </cities>
                           <phone>+44-22-2222 2222</phone>
                        </office>
                        <office id="BR5">
                           <names>
                              <name lang='en'>AAAAA Branch 4</name>
                           </names>
                           <address>
                              <line>7 Kings Highway</line>
                              <line>London</line>
                           </address>
                           <cities>
                              <city>London</city>
                           </cities>
                           <phone>+44-99-9999 9999</phone>
                        </office>
                        <office id="BR6">
                           <names>
                              <name lang='en'>Branch 4</name>
                           </names>
                           <address>
                              <line>22 Abbey Lane</line>
                              <line>London</line>
                           </address>
                           <cities>
                              <city>London</city>
                           </cities>
                           <phone>+44-55-555 5555</phone>
                        </office>
                     </offices>
                  </country>
               </countries>
            </region>
         </regions>
      </division>
   </divisions>
</locations>

...the xslt should produce the following result tree:

<?xml version="1.0" encoding="UTF-8"?>
<locations>
   <divisions>
      <division id="B">
         <regions>
            <region name="europe">
               <countries>
                  <country name="England">
                     <cities>
                        <city>
                           <name>London</name>
                           <offices>
                              <office>
                                 <names>
                                    <name lang='en'>AAAAA Branch 4</name>
                                 </names>
                                 <location>
                                   <address>
                                       <line>7 Kings Highway</line>
                                       <line>London</line>
                                    </address>
                                    <phone>+44-99-9999 9999</phone>
                                 </location>
                              </office>
                              <office>
                                 <names>
                                    <name lang='en'>Branch 4</name>
                                 </names>
                                 <location>
                                    <address>
                                       <line>22 Abbey Lane</line>
                                       <line>London</line>
                                    </address>
                                    <phone>+44-55-555 5555</phone>
                                 </location>
                                 <location>
                                    <address>
                                       <line>26 Abbey Lane</line>
                                       <line>London</line>
                                    </address>
                                    <phone>+44-22-2222 2222</phone>
                                 </location>
                              </office>
                           </offices>
                        </city>
                     </cities>
                  </country>
               </countries>
            </region>
         </regions>
      </division>
   </divisions>
</locations>

  The <office> elements contain all child elements where the 
office/names/name[(_at_)lang='en'] values match.  Further, a new element -- 
<location> has been added as a child to <office>.  The goal is to have only one 
<names> element under an <office> element for each unique occurrence of 
names/name where @lang='en' -- in other words, group all office locations by 
their common English office name, then copy all location-specific information 
for each office to the result tree as new <location> elements underneath the 
<office> element.

  My current XSLT is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:key name="by-city" match="temp/office/cities/city" use="."/>
   
<xsl:template match="/">
   <locations>
      <divisions>
         <xsl:for-each select="locations/divisions/division">
            <xsl:sort select="@id"/>
            <division id="{(_at_)id}">
               <regions>
                  <xsl:for-each select="regions/region">
                     <xsl:sort select="@name"/>
                     <region name="{(_at_)name}">
                        <countries>
                           <xsl:for-each select="countries/country">
                              <xsl:sort select="@name"/>
                              <country name="{(_at_)name}">
                                 <xsl:variable name="rtf">
                                    <temp>
                                       <xsl:copy-of select="offices/office"/>
                                    </temp>
                                 </xsl:variable>
                                 <xsl:for-each 
select="msxsl:node-set($rtf)/temp/office/cities/city[generate-id(.) = 
generate-id(key('by-city', .)[1])]">
                                    <xsl:sort select="."/>
                                    <cities>
                                       <city>
                                          <name>
                                             <xsl:value-of select="."/>
                                          </name>
                                          <offices>
                                                <xsl:for-each 
select="key('by-city', .)">
                                                   <xsl:sort 
select="../../names/name[(_at_)lang='en']"/>
                                                   <xsl:sort 
select="../../address"/>
                                                <xsl:if 
test="../../names/name[(_at_)lang='en'] 
!=../../preceding-sibling::names/name[(_at_)lang='en']">
                                                <!-- <office>  new <office> 
element should be inserted here -->  
                                                      <xsl:copy-of 
select="../../names"/>
                                                </xsl:if>
                                                   <location>
                                                      <xsl:copy-of 
select="../preceding-sibling::address"/>
                                                      <xsl:copy-of 
select="../following-sibling::phone"/>
                                                   </location>
                                                <!-- </office>  if the current 
node is the last in a group with the same /names/name[(_at_)lang='en'] value, 
                                                                    then 
closing tag should be inserted here -->  
                                                </xsl:for-each>
                                          </offices>
                                       </city>
                                    </cities>
                                 </xsl:for-each>
                              </country>
                           </xsl:for-each>
                        </countries>
                     </region>
                  </xsl:for-each>
               </regions>
            </division>
         </xsl:for-each>
      </divisions>
   </locations>
</xsl:template>
</xsl:stylesheet>

...and my result tree looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<locations>
   <divisions>
      <division id="B">
         <regions>
            <region name="europe">
               <countries>
                  <country name="England">
                     <cities>
                        <city>
                           <name>London</name>
                           <offices>
                              <location>
                                 <address>
                                    <line>7 Kings Highway</line>
                                    <line>London</line>
                                 </address>
                                 <phone>+44-99-9999 9999</phone>
                              </location>
                              <location>
                                 <address>
                                    <line>22 Abbey Lane</line>
                                    <line>London</line>
                                 </address>
                                 <phone>+44-55-555 5555</phone>
                              </location>
                              <location>
                                 <address>
                                    <line>26 Abbey Lane</line>
                                    <line>London</line>
                                 </address>
                                 <phone>+44-22-2222 2222</phone>
                              </location>
                           </offices>
                        </city>
                     </cities>
                  </country>
               </countries>
            </region>
         </regions>
      </division>
   </divisions>
</locations>

   The missing pieces are unique <names> elements (selected according to the 
above-specified criteria) and the appropriate placement of <office> elements 
(encapsulating <names> and <location> elements).

   I appreciate any help that can be offered!


Visit our website at http://www.ubs.com

This message contains confidential information and is intended only
for the individual named.  If you are not the named addressee you
should not disseminate, distribute or copy this e-mail.  Please
notify the sender immediately by e-mail if you have received this
e-mail by mistake and delete this e-mail from your system.

E-mail transmission cannot be guaranteed to be secure or error-free
as information could be intercepted, corrupted, lost, destroyed,
arrive late or incomplete, or contain viruses.  The sender therefore
does not accept liability for any errors or omissions in the contents
of this message which arise as a result of e-mail transmission.  If
verification is required please request a hard-copy version.  This
message is provided for informational purposes and should not be
construed as a solicitation or offer to buy or sell any securities or
related financial instruments.