Toon posts:

[xml/xsl] hierarchische boom

Pagina: 1
Acties:

Verwijderd

Topicstarter
ik heb de volgende xml data:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<categories>
  <category>
    <id>1</id>
    <name>cat1</name>
    <parentid>0</parentid>
  </category>
  <category>
    <id>2</id>
    <name>cat2</name>
    <parentid>1</parentid>
  </category>
  <category>
    <id>3</id>
    <name>cat3</name>
    <parentid>0</parentid>
  </category>
  <category>
    <id>4</id>
    <name>cat4</name>
    <parentid>3</parentid>
  </category>
  <category>
    <id>5</id>
    <name>cat5</name>
    <parentid>4</parentid>
  </category>
</categories

hiermee output ik de hierarchische tree met de volgende xsl syntax:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  <xsl:template match="categories">
    <table>
    <tr>
      <td>
        <ul><xsl:apply-templates select="category[parentcategoryid=0]"/></ul>
      </td>
    </tr>
    </table>
  </xsl:template>
  
  <xsl:template match="category">
    <xsl:choose>
    <xsl:when test="../category[parentcategoryid=current()/id]">
        <xsl:apply-templates select="current()" mode="isaparent"/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:apply-templates select="current()" mode="isnoparent"/>
    </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template match="category" mode="isaparent">
    <li>
      <xsl:value-of select="name"/><span> [</span><a href="category.asp?action=show&amp;what=category&amp;key={id}">view</a><span>/</span><a href="category.asp?action=edit&amp;what=category&amp;key={id}">edit</a><span>/</span><a href="category.asp?action=delete&amp;what=category&amp;key={id}">delete</a><span>]</span>
      <ul><xsl:apply-templates select="../category[parentcategoryid=current()/id]"/></ul>
    </li>
  </xsl:template>
  
  <xsl:template match="category" mode="isnoparent">
    <li>
      <xsl:value-of select="name"/><span> [</span><a href="category.asp?action=show&amp;what=category&amp;key={id}">view</a><span>/</span><a href="category.asp?action=edit&amp;what=category&amp;key={id}">edit</a><span>/</span><a href="category.asp?action=delete&amp;what=category&amp;key={id}">delete</a><span>]</span>
    </li>
  </xsl:template>

Dit gaat allemaal prima zolang er een topcategorie in de data zit, maar wat zodra ik een subset van de data hierarchisch wil uitprinten? Waar dus geen categorie in zit zonder parent (ie, waar parentid = 0)

Ik moet dus een xpath query bedenken die zegt: select een categorie, waar de parentid niet als id in de lijst voorkomt...

Heb van alles geprobeerd (oa category[not(../category/id=current()/parentcategoryid)]) maar tot nu toe zonder succes...

Verwijderd

Topicstarter
/me heeft het al zelf opgelost :z

category[not(../category/id=parentcategoryid)]

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Allereerst: waarom werk je niet met 'echte xml'? Dit is nogal relationele XML en dat is jammer :) . Uiteraard ontkom je hier niet aan als je dit als input krijgt of het uit een database haal (dat vermoed ik ;) ).

Ik zou in alle gevallen niet deze relationele XML naar een presentatie gaan transformeren. Waarom doe je dit niet in twee stappen?

(1) Eerst naar een hierarchie, dus simpel herschrijven
(2) Daarna pas presenteren?

Je kunt je hierarchie toevoeging dan mooi gebruiken voor andere doeleinden... Ik zal zo ook mijn voorbeeld maken :) .

Over je query. het lukte mij ook niet direct om daar een XPath expressie voor te schrijven omdat je dan als het ware naar het id van de category in je query moet verwijzen. Wel is het me uiteraard gelukt ;) .

Met deze input (voor het gemak een beetje aangepast)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0"?>

<categories>
  <category id="1" parentid="0">
    <name>cat1</name>
  </category>

  <category id="2" parentid="1">
    <name>cat2</name>
  </category>

  <category id="3" parentid="0">
    <name>cat3</name>
  </category>

  <category id="4" parentid="3">
    <name>cat4</name>
  </category>

  <category id="5" parentid="4">
    <name>cat5</name>
  </category>
</categories>

en deze stylesheet:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0"?>

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

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="categories">
      <categories>
        <xsl:for-each select="category">
            <xsl:if test="count( /categories/category[@id = current()/@parentid]) = 0">
              <xsl:apply-templates select="current()"/>
            </xsl:if>
        </xsl:for-each>
      </categories>
    </xsl:template>

    <xsl:template match="category">
      <category id="{@id}">
        <xsl:apply-templates/>

        <xsl:variable name="children" select="/categories/category[@parentid = current()/@id]"/>
        <xsl:if test="count($children) &gt; 0">
            <xsl:apply-templates select="$children"/>
        </xsl:if>
      </category>
    </xsl:template>

    <xsl:template match="name">
      <name>
        <xsl:value-of select="text()"/>
      </name>
    </xsl:template>

</xsl:stylesheet>

Krijg ik keurig deze output:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<categories>
    <category id="1">
      <name>cat1</name>

      <category id="2">
        <name>cat2</name>
      </category>
    </category>

    <category id="3">
      <name>cat3</name>
      
      <category id="4">
        <name>cat4</name>
            <category id="5">
              <name>cat5</name>
            </category>
      </category>
    </category>
</categories>

Als ik je goed begrijp was dit ongeveer wat je wilde :) . Merk op dat ik hier dus niets expliciet met de 0 doe! Als je op een detail punt iets anders wilt, moet het met dit voorbeeld zelf wel lukken denk ik...

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
KoekenBoes: /me heeft het al zelf opgelost :z
Tsss en ik maar prutsen ;) .
category[not(../category/id=parentcategoryid)]
Dat kan, maar helaas moet je dan nog steeds met die modes werken... Je moet dit namelijk altijd in het match attribute van een template zetten. Om toch de andere categorien te herschrijven moet je dus modes erbij pakken.

In mijn voorbeeld heb je duidelijkere herschrijfregels: Je herschrijft gewoon een category naar iets met children. Categories hetschrijf je door de category-en te selecteren die geen parent hebben. Ik vind dat wel een yummie oplossing :) .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
ik had in 'in den beginne' mijn xml al dmv een rucursieve functie in mijn asp code al naar 'echte xml' zoals jij het noemt omgezet, maar bedacht me toen dat dat eigenlijk niet nodig was aangezien je het in 1 keer met xsl goed kan omzetten. Vandaar dat ik het toen op deze manier deed.

Maar wat ik niet begrijp in jouw verhaal: Bij die nieuwe structuur die jij uitgepoept hebt, moet je toch ook met modes gaan werken om er een leuk hierarchisch html-lijstje van te maken? Of heb je een voorbeeldje waarmee dit niet hoeft? (waarschijnlijk stomme vraag ;))

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
KoekenBoes: maar bedacht me toen dat dat eigenlijk niet nodig was aangezien je het in 1 keer met xsl goed kan omzetten. Vandaar dat ik het toen op deze manier deed.
Dat is ook zeker zo :) . In XSLT kan het dus ook heel makkelijk zoals je in jouw en mijn voorbeeld kunt zien.
Maar wat ik niet begrijp in jouw verhaal: Bij die nieuwe structuur die jij uitgepoept hebt, moet je toch ook met modes gaan werken om er een leuk hierarchisch html-lijstje van te maken?
Nee, dat hoeft niet... Het voordeel van mijn 2-staps oplossing is dat je het maken van de hierarchie maar 1 keer hoeft te doen. Dit heeft twee voordelen: je transformatie naar een presentatie is duidelijker en je hoeft het niet opnieuw te doen als je een andere presentatie wilt :) . Je doet dus in feite 2 dingen tegelijk.
Of heb je een voorbeeldje waarmee dit niet hoeft? (waarschijnlijk stomme vraag ;))
idd ;) .

Ik zal even kijken of ik wat simpels kan bakken.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Ik heb even stiekem mijn stylesheet een klein beetje aangepast zodat er 'categories' om de sub-categorien heen komt te staan. Anders moest ik namelijk gaan iffen of er sub-categorien waren en daar had ik geen zin in :) .

Je krijgt dan deze output:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<categories>
    <category id="1">
      <name>cat1</name>
      <categories>
        <category id="2">
            <name>cat2</name>
        </category>
      </categories>
    </category>

    <category id="3">
      <name>cat3</name>
      <categories>
        <category id="4">
            <name>cat4</name>
            <categories>
              <category id="5">
                <name>cat5</name>
              </category>
            </categories>
        </category>
      </categories>
    </category>
</categories>

Beetje verbose, maar dat komt omdat er zo weinig andere info instaat.

De stylesheet is nu dit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0"?>

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

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="categories">
      <categories>
        <xsl:for-each select="category">
            <xsl:if test="count( /categories/category[@id = current()/@parentid]) = 0">
              <xsl:apply-templates select="current()"/>
            </xsl:if>
        </xsl:for-each>
      </categories>
    </xsl:template>

    <xsl:template match="category">
      <category id="{@id}">
        <xsl:apply-templates/>

        <xsl:variable name="children" select="/categories/category[@parentid = current()/@id]"/>

        <xsl:if test="count($children) &gt; 0">
            <categories>
              <xsl:apply-templates select="$children"/>
            </categories>
        </xsl:if>
      </category>
    </xsl:template>

    <xsl:template match="name">
      <name>
        <xsl:value-of select="text()"/>
      </name>
    </xsl:template>

</xsl:stylesheet>

De stylesheet naar html is nu triviaal:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0"?>

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

    <xsl:output method="xml" indent="yes"
                doctype-public="-//W3C//DTD XHTML 1.1//EN"
                doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"/>

    <xsl:template match="/">
      <html>
        <head>
            <title>Stoere Categorien</title>
        </head>

        <body>
            <xsl:apply-templates/>
        </body>
      </html>
    </xsl:template>

    <xsl:template match="categories">
      <ul>
        <xsl:apply-templates select="category"/>
      </ul>
    </xsl:template>

    <xsl:template match="category">
      <li>
        Category: <xsl:value-of select="name/text()"/>
        <xsl:apply-templates select="categories"/>
      </li>
    </xsl:template>
</xsl:stylesheet>

en tadaaaa:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>Stoere Categorien</title>
    </head>

    <body>
      <ul>
        <li>Category: cat1
            <ul>
              <li>Category: cat2</li>
            </ul>
        </li>
        <li>Category: cat3
            <ul>
              <li>Category: cat4
                <ul>
                    <li>Category: cat5</li>
                </ul>
              </li>
            </ul>
        </li>
      </ul>
    </body>
</html>

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment

Pagina: 1