[xsl] WG: Nested Grouping of "flat" input

Subject: [xsl] WG: Nested Grouping of "flat" input
From: Thomas Zöchling <thomas.zoechling@xxxxxx>
Date: Thu, 9 Dec 2004 12:08:13 +0100
Hi List!
I am working on a bookmark manager, that is built around  XBEL
(http://pyxml.sourceforge.net/topics/xbel/).
The server-side part of our application needs to store the bookmark data in
a relational database.
But as XBEL defines a nested hierarchy, which cannot be stored in the
database directly, we had to introduce a flat version of the XBEL data.
Now our problem is, that we need to go the other way round, and extract the
path of a bookmark from our flat xml file, and restore the XBEL hierarchy
from it.

User( browser) -> XBEL -> flat format -> database -> flat -> XBEL -> all
other formats

Summary: Create a XML structured hierarchy from a path.

Examples:

XBEL:

<?xml version="1.0" encoding="UTF-8"?>
<xbel version="1.0">
<title>Bookmarks</title>
<older folded="yes">
<title>Sample2</title>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"><title>Mozilla Firefox Startseite</title></bookmark>
</folder>
<folder folded="yes">
<title>Sample</title>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"><title>Test</title></bookmark>
<folder folded="yes">
<title>FolderInSample</title>
</folder>
</folder>
<folder folded="yes">
<title>Bookmarks Toolbar Folder</title>
<bookmark href="http://slashdot.org/";><title>Slashdot: News for nerds, stuff
that matters</title></bookmark>
<bookmark href="http://www.heise.de/";><title>heise online</title></bookmark>
<bookmark href="http://futurezone.orf.at/";><title>ORF ON
Futurezone</title></bookmark>
</folder>
</xbel>

Flat XBEL:

<?xml version="1.0" encoding="utf-8"?>
<xbelflat version="1.0">
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="Mozilla Firefox Startseite"
pfad="Bookmarks/Sample2/"></bookmark>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="Test" pfad="Bookmarks/Sample/"></bookmark>
<bookmark href="http://slashdot.org/"; name="Slashdot: News for nerds, stuff
that matters" pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
<bookmark href="http://www.heise.de/"; name="heise online"
pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
<bookmark href="http://futurezone.orf.at/"; name="ORF ON Futurezone"
pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
</xbelflat>

The xbelflat should be transformed back to XBEL:

What I can now:
- Split the path (@pfad) and output it in folders
- Group the bookmarks

What I cannot:
- Do the whole transform in one stylesheet
- Recreate the correct hierarchy (currently everything is flat grouped)


XSLT stylesheets:

Extracts the folder Names from the @pfad (=path) attribute

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:output indent="yes"/>
  <xsl:template name="root" match="xbelflat">
            <xbelflat version="1.0">
                        <xsl:call-template name="bm"></xsl:call-template>
            </xbelflat>
  </xsl:template>

  <xsl:template name="bm">
            <xsl:for-each select="bookmark">
                        <xsl:apply-templates/>

                        <xsl:call-template name="links">
                           <xsl:with-param name="str" select="@pfad"/>
                           <xsl:with-param name="pos"
select="position()-1"/>
                        </xsl:call-template>
        </xsl:for-each>
        <xsl:apply-templates/>
  </xsl:template>

  <xsl:template name="links">
    <xsl:param name="str"/>
    <xsl:param name="pos"/>
    <xsl:variable name="bmName" select="@name"/>

    <xsl:if test="position() != 1 ">

    </xsl:if>

    <xsl:choose>
      <xsl:when test="contains($str,'/') and
$bmName!=substring-before($str,'/')">
       <folder>
                        <xsl:attribute name="pfad"><xsl:value-of
select="substring-before($str,'/')"/></xsl:attribute>
        <xsl:call-template name="links">
          <xsl:with-param name="str" select="substring-after($str,'/')"/>
        </xsl:call-template>
        </folder>
      </xsl:when>
      <xsl:otherwise>
                        <xsl:attribute name="href"><xsl:value-of
select="@href"></xsl:value-of></xsl:attribute><xsl:value-of
select="following-sibling"/>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Output of the above

<?xml version="1.0" encoding="UTF-16"?>
<xbelflat version="1.0">
<folder pfad="Bookmarks">
<folder pfad="Sample2"
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Sample"
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://slashdot.org/";></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://www.heise.de/";></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://futurezone.orf.at/";></folder>
</folder>
</xbelflat>

Second Stylesheet:
Groups the Bookmarks (But any structure information is lost here)

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:key name="distinct" match="folder" use="@pfad"/>
<xsl:template match="/">
<xbel version="1.0">

<!--<xsl:for-each select="/xbelflat/folder/folder/folder[generate-id(.) =
generate-id(key('distinct',@pfad))]"> -->
<xsl:for-each select="xbelflat//folder[generate-id(.) =
generate-id(key('distinct',@pfad))]">

<folder>
                                   <xsl:value-of select="@pfad"/>
                        <xsl:variable name="currPath">
                                   <xsl:value-of select="@pfad"/>
                        </xsl:variable>
                        <xsl:variable name="lstBookmark"
select="//folder[@pfad=$currPath]"/>
                        <xsl:call-template name="DistinctedBookmarks">
                                   <xsl:with-param name="lstBookmark"
select="$lstBookmark" />
                        </xsl:call-template>
</folder>
            </xsl:for-each>
</xbel>
</xsl:template>

<xsl:template name="DistinctedBookmarks">
            <xsl:param name="lstBookmark"/>
            <xsl:for-each select="$lstBookmark">
                        <xsl:if test="@href!=''">
                                   <xsl:element name="bookmark">

                                                           <xsl:attribute
name="href"><xsl:value-of select="@href"/></xsl:attribute>
                                                           <xsl:attribute
name="name"><xsl:value-of select="@name"/></xsl:attribute>

                                   </xsl:element>
                                   </xsl:if>
                          </xsl:for-each>
</xsl:template>

</xsl:stylesheet>


Output:
Hi List!
I am working on a bookmark manager, that is built around  XBEL
(http://pyxml.sourceforge.net/topics/xbel/).
The server-side part of our application needs to store the bookmark data in
a relational database.
But as XBEL defines a nested hierarchy, which cannot be stored in the
database directly, we had to introduce a flat version of the XBEL data.
Now our problem is, that we need to go the other way round, and extract the
path of a bookmark from our flat xml file, and restore the XBEL hierarchy
from it.

User( browser) -> XBEL -> flat format -> database -> flat -> XBEL -> all
other formats

Summary: Create a XML structured hierarchy from a path.

Examples:

XBEL:

<?xml version="1.0" encoding="UTF-8"?>
<xbel version="1.0">
<title>Bookmarks</title>
<older folded="yes">
<title>Sample2</title>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"><title>Mozilla Firefox Startseite</title></bookmark>
</folder>
<folder folded="yes">
<title>Sample</title>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"><title>Test</title></bookmark>
<folder folded="yes">
<title>FolderInSample</title>
</folder>
</folder>
<folder folded="yes">
<title>Bookmarks Toolbar Folder</title>
<bookmark href="http://slashdot.org/";><title>Slashdot: News for nerds, stuff
that matters</title></bookmark>
<bookmark href="http://www.heise.de/";><title>heise online</title></bookmark>
<bookmark href="http://futurezone.orf.at/";><title>ORF ON
Futurezone</title></bookmark>
</folder>
</xbel>

Flat XBEL:

<?xml version="1.0" encoding="utf-8"?>
<xbelflat version="1.0">
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="Mozilla Firefox Startseite"
pfad="Bookmarks/Sample2/"></bookmark>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="Test" pfad="Bookmarks/Sample/"></bookmark>
<bookmark href="http://slashdot.org/"; name="Slashdot: News for nerds, stuff
that matters" pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
<bookmark href="http://www.heise.de/"; name="heise online"
pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
<bookmark href="http://futurezone.orf.at/"; name="ORF ON Futurezone"
pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
</xbelflat>

The xbelflat should be transformed back to XBEL:

What I can now:
- Split the path (@pfad) and output it in folders
- Group the bookmarks

What I cannot:
- Do the whole transform in one stylesheet
- Recreate the correct hierarchy (currently everything is flat grouped)


XSLT stylesheets:

Extracts the folder Names from the @pfad (=path) attribute

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:output indent="yes"/>
  <xsl:template name="root" match="xbelflat">
            <xbelflat version="1.0">
                        <xsl:call-template name="bm"></xsl:call-template>
            </xbelflat>
  </xsl:template>

  <xsl:template name="bm">
            <xsl:for-each select="bookmark">
                        <xsl:apply-templates/>

                        <xsl:call-template name="links">
                           <xsl:with-param name="str" select="@pfad"/>
                           <xsl:with-param name="pos"
select="position()-1"/>
                        </xsl:call-template>
        </xsl:for-each>
        <xsl:apply-templates/>
  </xsl:template>

  <xsl:template name="links">
    <xsl:param name="str"/>
    <xsl:param name="pos"/>
    <xsl:variable name="bmName" select="@name"/>

    <xsl:if test="position() != 1 ">

    </xsl:if>

    <xsl:choose>
      <xsl:when test="contains($str,'/') and
$bmName!=substring-before($str,'/')">
       <folder>
                        <xsl:attribute name="pfad"><xsl:value-of
select="substring-before($str,'/')"/></xsl:attribute>
        <xsl:call-template name="links">
          <xsl:with-param name="str" select="substring-after($str,'/')"/>
        </xsl:call-template>
        </folder>
      </xsl:when>
      <xsl:otherwise>
                        <xsl:attribute name="href"><xsl:value-of
select="@href"></xsl:value-of></xsl:attribute><xsl:value-of
select="following-sibling"/>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Output of the above

<?xml version="1.0" encoding="UTF-16"?>
<xbelflat version="1.0">
<folder pfad="Bookmarks">
<folder pfad="Sample2"
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Sample"
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://slashdot.org/";></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://www.heise.de/";></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://futurezone.orf.at/";></folder>
</folder>
</xbelflat>

Second Stylesheet:
Groups the Bookmarks (But any structure information is lost here)

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:key name="distinct" match="folder" use="@pfad"/>
<xsl:template match="/">
<xbel version="1.0">

<!--<xsl:for-each select="/xbelflat/folder/folder/folder[generate-id(.) =
generate-id(key('distinct',@pfad))]"> -->
<xsl:for-each select="xbelflat//folder[generate-id(.) =
generate-id(key('distinct',@pfad))]">

<folder>
                                   <xsl:value-of select="@pfad"/>
                        <xsl:variable name="currPath">
                                   <xsl:value-of select="@pfad"/>
                        </xsl:variable>
                        <xsl:variable name="lstBookmark"
select="//folder[@pfad=$currPath]"/>
                        <xsl:call-template name="DistinctedBookmarks">
                                   <xsl:with-param name="lstBookmark"
select="$lstBookmark" />
                        </xsl:call-template>
</folder>
            </xsl:for-each>
</xbel>
</xsl:template>

<xsl:template name="DistinctedBookmarks">
            <xsl:param name="lstBookmark"/>
            <xsl:for-each select="$lstBookmark">
                        <xsl:if test="@href!=''">
                                   <xsl:element name="bookmark">

                                                           <xsl:attribute
name="href"><xsl:value-of select="@href"/></xsl:attribute>
                                                           <xsl:attribute
name="name"><xsl:value-of select="@name"/></xsl:attribute>

                                   </xsl:element>
                                   </xsl:if>
                          </xsl:for-each>
</xsl:template>

</xsl:stylesheet>


Output:
Hi List!
I am working on a bookmark manager, that is built around  XBEL
(http://pyxml.sourceforge.net/topics/xbel/).
The server-side part of our application needs to store the bookmark data in
a relational database.
But as XBEL defines a nested hierarchy, which cannot be stored in the
database directly, we had to introduce a flat version of the XBEL data.
Now our problem is, that we need to go the other way round, and extract the
path of a bookmark from our flat xml file, and restore the XBEL hierarchy
from it.

User( browser) -> XBEL -> flat format -> database -> flat -> XBEL -> all
other formats

Summary: Create a XML structured hierarchy from a path.

Examples:

XBEL:

<?xml version="1.0" encoding="UTF-8"?>
<xbel version="1.0">
<title>Bookmarks</title>
<older folded="yes">
<title>Sample2</title>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"><title>Mozilla Firefox Startseite</title></bookmark>
</folder>
<folder folded="yes">
<title>Sample</title>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"><title>Test</title></bookmark>
<folder folded="yes">
<title>FolderInSample</title>
</folder>
</folder>
<folder folded="yes">
<title>Bookmarks Toolbar Folder</title>
<bookmark href="http://slashdot.org/";><title>Slashdot: News for nerds, stuff
that matters</title></bookmark>
<bookmark href="http://www.heise.de/";><title>heise online</title></bookmark>
<bookmark href="http://futurezone.orf.at/";><title>ORF ON
Futurezone</title></bookmark>
</folder>
</xbel>

Flat XBEL:

<?xml version="1.0" encoding="utf-8"?>
<xbelflat version="1.0">
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="Mozilla Firefox Startseite"
pfad="Bookmarks/Sample2/"></bookmark>
<bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="Test" pfad="Bookmarks/Sample/"></bookmark>
<bookmark href="http://slashdot.org/"; name="Slashdot: News for nerds, stuff
that matters" pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
<bookmark href="http://www.heise.de/"; name="heise online"
pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
<bookmark href="http://futurezone.orf.at/"; name="ORF ON Futurezone"
pfad="Bookmarks/Bookmarks Toolbar Folder/"></bookmark>
</xbelflat>

The xbelflat should be transformed back to XBEL:

What I can now:
- Split the path (@pfad) and output it in folders
- Group the bookmarks

What I cannot:
- Do the whole transform in one stylesheet
- Recreate the correct hierarchy (currently everything is flat grouped)


XSLT stylesheets:

Extracts the folder Names from the @pfad (=path) attribute

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:output indent="yes"/>
  <xsl:template name="root" match="xbelflat">
            <xbelflat version="1.0">
                        <xsl:call-template name="bm"></xsl:call-template>
            </xbelflat>
  </xsl:template>

  <xsl:template name="bm">
            <xsl:for-each select="bookmark">
                        <xsl:apply-templates/>

                        <xsl:call-template name="links">
                           <xsl:with-param name="str" select="@pfad"/>
                           <xsl:with-param name="pos"
select="position()-1"/>
                        </xsl:call-template>
        </xsl:for-each>
        <xsl:apply-templates/>
  </xsl:template>

  <xsl:template name="links">
    <xsl:param name="str"/>
    <xsl:param name="pos"/>
    <xsl:variable name="bmName" select="@name"/>

    <xsl:if test="position() != 1 ">

    </xsl:if>

    <xsl:choose>
      <xsl:when test="contains($str,'/') and
$bmName!=substring-before($str,'/')">
       <folder>
                        <xsl:attribute name="pfad"><xsl:value-of
select="substring-before($str,'/')"/></xsl:attribute>
        <xsl:call-template name="links">
          <xsl:with-param name="str" select="substring-after($str,'/')"/>
        </xsl:call-template>
        </folder>
      </xsl:when>
      <xsl:otherwise>
                        <xsl:attribute name="href"><xsl:value-of
select="@href"></xsl:value-of></xsl:attribute><xsl:value-of
select="following-sibling"/>
        <xsl:apply-templates/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Output of the above

<?xml version="1.0" encoding="UTF-16"?>
<xbelflat version="1.0">
<folder pfad="Bookmarks">
<folder pfad="Sample2"
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Sample"
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official"></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://slashdot.org/";></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://www.heise.de/";></folder>
</folder>
<folder pfad="Bookmarks">
<folder pfad="Bookmarks Toolbar Folder"
href="http://futurezone.orf.at/";></folder>
</folder>
</xbelflat>

Second Stylesheet:
Groups the Bookmarks (But any structure information is lost here)

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:key name="distinct" match="folder" use="@pfad"/>
<xsl:template match="/">
<xbel version="1.0">

<!--<xsl:for-each select="/xbelflat/folder/folder/folder[generate-id(.) =
generate-id(key('distinct',@pfad))]"> -->
<xsl:for-each select="xbelflat//folder[generate-id(.) =
generate-id(key('distinct',@pfad))]">

<folder>
                                   <xsl:value-of select="@pfad"/>
                        <xsl:variable name="currPath">
                                   <xsl:value-of select="@pfad"/>
                        </xsl:variable>
                        <xsl:variable name="lstBookmark"
select="//folder[@pfad=$currPath]"/>
                        <xsl:call-template name="DistinctedBookmarks">
                                   <xsl:with-param name="lstBookmark"
select="$lstBookmark" />
                        </xsl:call-template>
</folder>
            </xsl:for-each>
</xbel>
</xsl:template>

<xsl:template name="DistinctedBookmarks">
            <xsl:param name="lstBookmark"/>
            <xsl:for-each select="$lstBookmark">
                        <xsl:if test="@href!=''">
                                   <xsl:element name="bookmark">

                                                           <xsl:attribute
name="href"><xsl:value-of select="@href"/></xsl:attribute>
                                                           <xsl:attribute
name="name"><xsl:value-of select="@name"/></xsl:attribute>

                                   </xsl:element>
                                   </xsl:if>
                          </xsl:for-each>
</xsl:template>

</xsl:stylesheet>


Output:

<?xml version="1.0" encoding="UTF-16"?><xbel version="1.0">
<folder>Bookmarks</folder>
<folder>Sample2
            <bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="" /></folder>
<folder>Sample
            <bookmark
href="http://www.google.at/firefox?client=firefox-a&amp;rls=org.mozilla:de-D
E:official" name="" /></folder>
<folder>Bookmarks Toolbar Folder<bookmark href="http://slashdot.org/";
name="" />
            <bookmark href="http://www.heise.de/"; name="" />
            <bookmark href="http://futurezone.orf.at/"; name="" /></folder>
</xbel>


How can I get back a Tee structure?

Bookmarks
            Sample2
            Sample
                        .
            BookmarkToolbarFolder
                        .
                        .


Thanks in advance

Thomas

Current Thread