sequentiell in hierarchisch wandeln

sanovision

Grünschnabel
Hi,
ich muss mittels XSLT2.0 aus einer sequentiellen Struktur eine hierarchische produzieren.
Die Hierarchieinformation kommt aus dem Attribut "hier" welches den Titel des Elternkapitel enthält :
Code:
<kap>
<title>Kapitel 1</title>
Hier steht der Text</kap>
<kap>
<title>Kapitel 2</title>
Hier steht der Text
</kap>
<kap hier="Kapitel 2">
<title>Kapitel 2.1</title>
Hier steht der Text
</kap>
<kap hier="Kapitel 2">
<title>Kapitel 2.2</title>
Hier steht der Text
</kap>
<kap hier="Kapitel 2.2">
<title>Kapitel 2.2.1</title>
Hier steht der Text
</kap>
<kap hier="Kapitel 2.2">
<title>Kapitel 2.2.2</title>
Hier steht der Text
</kap>
<kap>
<title>Kapitel 3</title>
Hier steht der Text
</kap>
Das Ganze soll dann so aussehen:
Code:
<kap title="Kapitel 1"/>
<kap title="Kapitel 2">
   <kap title="Kapitel 2.1"/>
   <kap title="Kapitel 2.2">
        <kap title="Kapitel 2.2.1"/>
        <kap title="Kapitel 2.2.2"/>
   </kap>
</kap>
<kap title="Kapitel 3"/>

ich habe zwar einen umständlichen Weg gefunden, der hat aber den Nachteil, dass die Hierarchietiefe vom Stylesheet abhängig ist.

Dabei schaut das Stylesheet bei der Produktion eines Kapitels, ob es nachfolgende Kapitel mit einem Attribut "hier" gibt, die den gleichen Wert wie der Titel des gegenwärtigen Kapitels haben.

Für jede Schachtelungstiefe muss das Stylesheet nachsehen, ob es eventuell Unterkapitel gibt. So schaut das stylesheet aus:
Code:
<xsl:for-each select="root/*">
<!--
1. EBENE
testet ob das Topic KEIN "hier"-Attribut hat, also auf oberster Ebene steht. -->

  <xsl:if test="not(@hier)">
      <xsl:variable name="kap1" select="./title" />

         <kap>

		<xsl:attribute name="title">
			<xsl:value-of select="./title" />
		</xsl:attribute>

<!--
2.EBENE
testet, ob es Topics im Dokument gibt, die ein "hier"
Att haben, dass den selben Inhalt hat wie der gegenwärtige Topic-Titel hat. -->

		<xsl:for-each select="//* [@hier = $kap1]">
			<xsl:variable name="kap2" select="./title" />

			<kap>

				<xsl:attribute name="title">
					<xsl:value-of select="./title" />
				</xsl:attribute>

	
<!--
3.EBENE
 -->
			<xsl:for-each select="//* [@hier = $kap2]">
				<xsl:variable name="kap3" select="./title" />
								
				<kap>

					<xsl:attribute name="title">
						<xsl:value-of select="./title" />
					</xsl:attribute>

<!--
4.EBENE
-->

					<xsl:for-each
...
...
...
Hat jemand eine Idee wie man das sauberer löst, so dass nicht für jede Schachtelungstiefe wieder eine For-Each-Schleife gebaut werden muss?
 
Ahh, wunderschönes Problem. Hier meine Lösung mit XSLT 1.0

Code:
<?xml version="1.0"?>

<root>
    <kap>
	<title>Kapitel 1</title>
	Hier steht der Text
   </kap>
   <kap>
	<title>Kapitel 2</title>
	Hier steht der Text
   </kap>

   <kap hier="Kapitel 2">
	<title>Kapitel 2.1</title>
	Hier steht der Text
    </kap>
    <kap hier="Kapitel 2">
	<title>Kapitel 2.2</title>
	Hier steht der Text
    </kap>
    <kap hier="Kapitel 2.2">
	<title>Kapitel 2.2.1</title>
	Hier steht der Text
    </kap>
    <kap hier="Kapitel 2.2">
	<title>Kapitel 2.2.2</title>
        Hier steht der Text
     </kap>
     <kap>
	<title>Kapitel 3</title>
        Hier steht der Text
     </kap>
</root>
Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xslt:stylesheet xmlns:xslt="http://www.w3.org/1999/XSL/Transform" version="1.0">

	
	
	<xslt:template match="/root">
		<root>
		<xslt:apply-templates select="kap[not(@hier)]"/>
		</root>
	</xslt:template>
	
	<xslt:template match="kap">
		<kap>
		<xslt:if test="@hier">
			<xslt:attribute name="hier">
				<xslt:value-of select="@hier" />
			</xslt:attribute>	
		</xslt:if>
		<!-- wert des Elements ausgeben -->
		<title><xslt:value-of select="title" /></title>
		<xslt:value-of select="." />
		<xslt:variable name="Label" select="title" />
		<xslt:apply-templates select="../kap[@hier=$Label]"/>
		</kap>
	</xslt:template>
	
	
	

	
	
</xslt:stylesheet>
Ausgabe
Code:
<?xml version="1.0" encoding="UTF-16"?>
<root>
   <kap><title>Kapitel 1</title>
	Kapitel 1
	Hier steht der Text
   </kap><kap><title>Kapitel 2</title>
	Kapitel 2
	Hier steht der Text
   <kap hier="Kapitel 2"><title>Kapitel 2.1</title>
	Kapitel 2.1
	Hier steht der Text
    </kap><kap hier="Kapitel 2"><title>Kapitel 2.2</title>
	Kapitel 2.2
	Hier steht der Text
    <kap hier="Kapitel 2.2"><title>Kapitel 2.2.1</title>
	Kapitel 2.2.1
	Hier steht der Text
    </kap><kap hier="Kapitel 2.2"><title>Kapitel 2.2.2</title>
	Kapitel 2.2.2
        Hier steht der Text
     </kap></kap></kap><kap><title>Kapitel 3</title>
	Kapitel 3
        Hier steht der Text
     </kap>
</root>

EDIT: btw Ein apply-template ist eine implizite Schleife. Eigentlich immer lassen sich Probleme damit besser lösen als mit for-each. Obwohl es mit Schleifen genauso ginge. Dein Ansatz mit den Schleifen nennt man PUSH-Verfahren, ich bevorzuge dass PULL Verfahren. Das führt zu mehr Templates, ist aber besser zu warten, stabiler gegeüber Änderungen der XML Struktur und kann leichter includiert werden.

siehe auch hier:

http://forum.de.selfhtml.org/archiv/2002/10/t26758/
 
Zuletzt bearbeitet:
Hallo,

@limago
hab den Code nochmal leicht geändert. Jetzt müsste es die Ausgabe sein, so wie er es möchte. Hoffe es war keine Absicht von dir, dass der Code nicht so aussieht. :)

XML:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xslt:stylesheet xmlns:xslt="http://www.w3.org/1999/XSL/Transform" version="1.0">

	
	
	<xslt:template match="/root">
		<root>
		<xslt:apply-templates select="kap[not(@hier)]"/>
		</root>
	</xslt:template>
	
	<xslt:template match="kap">
		<kap>
		<xslt:attribute name="title">
			<xslt:value-of select="title" />
		</xslt:attribute>	
		
		<!-- wert des Elements ausgeben -->
		<xslt:value-of select="." />
		<xslt:variable name="Label" select="title" />
		<xslt:apply-templates select="../kap[@hier=$Label]"/>
		</kap>
	</xslt:template>
	
</xslt:stylesheet>


MFG

zEriX
 
Zuletzt bearbeitet von einem Moderator:
Doch, das war Absicht! Du unterschägst ja den Titel ;-)

Ich denke, den hat er nur aus Bequemlichkeit wegelassen. Er will ja aus der Sequenziellen eine gleiche hierachische Liste machen, wenn ich das recht verstanden habe.

Grüße

edit: meine Lösung im IE:

Code:
<?xml version="1.0" encoding="UTF-16" ?> 
- <root>
- <kap>
  <title>Kapitel 1</title> 
  Kapitel 1 Hier steht der Text 
  </kap>
- <kap>
  <title>Kapitel 2</title> 
  Kapitel 2 Hier steht der Text 
- <kap hier="Kapitel 2">
  <title>Kapitel 2.1</title> 
  Kapitel 2.1 Hier steht der Text 
  </kap>
- <kap hier="Kapitel 2">
  <title>Kapitel 2.2</title> 
  Kapitel 2.2 Hier steht der Text 
- <kap hier="Kapitel 2.2">
  <title>Kapitel 2.2.1</title> 
  Kapitel 2.2.1 Hier steht der Text 
  </kap>
- <kap hier="Kapitel 2.2">
  <title>Kapitel 2.2.2</title> 
  Kapitel 2.2.2 Hier steht der Text 
  </kap>
  </kap>
  </kap>
- <kap>
  <title>Kapitel 3</title> 
  Kapitel 3 Hier steht der Text 
  </kap>
  </root>

Ich sehe da keinen Fehler!
 
Zuletzt bearbeitet:
Also um nach seinem Beispiel zu gehen, soll der Titel als Attribut da vorhanden sein und nicht mehr als Element und das Attribut hier soll gar nicht mehr vorhanden sein, deshalb soll es ja dann hierarchisch sein.

Beispiel:
XML:
<kap title="Kapitel 1"/>
<kap title="Kapitel 2">
   <kap title="Kapitel 2.1"/>
   <kap title="Kapitel 2.2">
        <kap title="Kapitel 2.2.1"/>
        <kap title="Kapitel 2.2.2"/>
   </kap>
</kap>
<kap title="Kapitel 3"/>


Meine Ausgabe:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<root><kap title="Kapitel 1">
	Kapitel 1
	Hier steht der Text
   </kap><kap title="Kapitel 2">
	Kapitel 2
	Hier steht der Text
   <kap title="Kapitel 2.1">
	Kapitel 2.1
	Hier steht der Text
    </kap><kap title="Kapitel 2.2">
	Kapitel 2.2
	Hier steht der Text
    <kap title="Kapitel 2.2.1">
	Kapitel 2.2.1
	Hier steht der Text
    </kap><kap title="Kapitel 2.2.2">
	Kapitel 2.2.2
        Hier steht der Text
     </kap></kap></kap><kap title="Kapitel 3">
	Kapitel 3
        Hier steht der Text
     </kap></root>

Ok, es ist noch nicht ganz perfekt, da steht nämlich immer noch der Titel nochmal im Text. :-(
 
Zuletzt bearbeitet von einem Moderator:
Hey, das mit dem Titel ist echt ein Problem. Welchen Transformator benutzt Du? Ich verwende MSXSL und den Parser des IE 7. Scheint ein bug zu sein weil auch

<xslt:value-of select="text()" />

statt

<xslt:value-of select="." />

zum selben Ergebnis führt. Das darf nicht sein. Entweder muss der Text auch in ein Tag, gepackt werden, oder man sollte es mal mit einem anderen Transformator (XALAN) oder anderen versuchen.

Verwirrt

Jo
 
Ich benutze den normalen Java-Transformator.

Wenn ich text() benutze bekomme ich gar keine Ausgabe, von dem Text zwischen den Tags.

XML:
<?xml version="1.0" encoding="UTF-8"?><root><kap title="Kapitel 1">
	</kap><kap title="Kapitel 2">
	<kap title="Kapitel 2.1">
	</kap><kap title="Kapitel 2.2">
	<kap title="Kapitel 2.2.1">
	</kap><kap title="Kapitel 2.2.2">
	</kap></kap></kap><kap title="Kapitel 3">
	</kap></root>

MFG
 
Zuletzt bearbeitet von einem Moderator:
Super, das hat mich auf den richtigen Weg gebracht. Habe aber zunächst noch einen etwas anderen Lösungsweg gefunden:
Ein rekursives Template:
Code:
<xsl:template name="root">

    <xsl:for-each select="root/*[not(@hier)]">
	<xsl:call-template name="Hierarchie" />
    </xsl:for-each>

</xsl:template>


<xsl:template name="Hierarchie">
		
    <kap>
	<xsl:variable name="kapTit" select="./title" />
        <xsl:attribute name="title">
            <xsl:value-of select="./title" />
        </xsl:attribute>
								
        <xsl:choose>
            <xsl:when test="following-sibling::*[@hier = $kapTit]">
			
	        <xsl:for-each select="following-sibling::*[@hier = $kapTit]">
		    <xsl:call-template name="ditamapHier"/>
	        </xsl:for-each>
			
	    </xsl:when>
        </xsl:choose>
    </kap>
</xsl:template>
Aber Eure Lösung ist ja noch simpler, warum denkt man immer so kompliziert? Hab den Eindruck, es ist oft einfacher die komplexen Wege zu finden als die simplen!?

graz
 
Uff! Habe auch das Problem mit dem Text gelöst. War schieriger als der Rest

XML:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xslt:stylesheet xmlns:xslt="http://www.w3.org/1999/XSL/Transform" version="1.0">
             <xslt:template match="/root">
                     <root>
                             <xslt:apply-templates select="kap[not(@hier)]"/>
                     </root>
             </xslt:template>
             
             <xslt:template match="kap">
                     <kap>
                     	    
                             <xslt:attribute name="title">
                                         <xslt:value-of select="title" />
                             </xslt:attribute> 
                             <!-- wert des Elements ausgeben -->
                             <xslt:apply-templates select="text()"/>
                             <xslt:variable name="Label" select="title" />
                             <xslt:apply-templates select="../kap[@hier=$Label]"/>
                      </kap>
            </xslt:template>
            
             <xslt:template match="text()[normalize-space(.)][../*]">
        	<xslt:value-of select="."/>
    	     </xslt:template>


            
</xslt:stylesheet>
 
Zuletzt bearbeitet von einem Moderator:

Neue Beiträge

Zurück