Umwandlung von einer flachen xml Struktur in html Listen Elemente ul bzw. li

halmarius

Grünschnabel
Hallo,

hab ein Problem mit der Umwandlung von einer flachen xml Struktur in html Listen Elemente ul bzw. li.
Ich hab ein xsl Beispiel im Internet gefunden, dass ich adaptiert habe und das auch funktioniert. Nur leider hab ich jetzt festgestellt, das der Output nicht xhtml transitional konform ist.
Hier ein Beispiel Input:
HTML:
...
		<kom>irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></kom>
		<kom ty="aufz" eb="1">Das ist der erste Punkt</kom>
		<kom ty="aufz" eb="1">Das ist der zweite Punkt</kom>
		<kom ty="aufz" eb="2">Das ist der erste Punkt der <f>zweiten</f> Ebene</kom>
		<kom ty="aufz" eb="3">Das ist der erste Punkt der <f>dritten</f> Ebene</kom>
		<kom ty="aufz" eb="1">Das ist der dritte Punkt</kom>
		<kom ty="aufz" eb="1">Das ist der vierte Punkt</kom>
		<kom ty="oNr">irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></kom>
...

Das xsl das dies derzeit konvertiert:
HTML:
.....
<xsl:template match="kom">
	<xsl:choose>
		<xsl:when test="@ty='aufz' or @ty='aufz_num'">
			<xsl:apply-templates />
		</xsl:when>
	  
		<xsl:otherwise>
			<p class="content">
				<xsl:apply-templates />
			</p>
		</xsl:otherwise>
	</xsl:choose>
</xsl:template>
.....
HTML:
<xsl:key name='listitems1_ul' match="kom[@eb='1' and preceding-sibling::kom[1][@eb='1' or @eb='2' or @eb='3'] and @ty='aufz']"
    use="generate-id(preceding-sibling::kom[@eb='1'][1])"/>
 
 <xsl:key name='listitems2_ul' match="kom[@eb='2' and preceding-sibling::kom[1][@eb='2' or @eb='3'] and @ty='aufz']"
    use="generate-id(preceding-sibling::kom[@eb='2' and preceding-sibling::kom[1][@eb='1']][1])"/>
 
 <xsl:key name='listitems3_ul' match="kom[@eb='3' and preceding-sibling::kom[1][@eb='3'] and @ty='aufz']"
    use="generate-id(preceding-sibling::kom[@eb='3' and preceding-sibling::kom[1][@eb='2']][1])"/>
 
 <!--First Items in sublists-->
 <xsl:key name='sublistitems2_ul' match="kom[@eb='2' and preceding-sibling::kom[1][@eb='1'] and @ty='aufz']"
    use="generate-id(preceding-sibling::kom[@eb='1'][1])"/>
 
 <xsl:key name='sublistitems3_ul' match="kom[@eb='3' and preceding-sibling::kom[1][@eb='2'] and @ty='aufz']"
    use="generate-id(preceding-sibling::kom[@eb='2'][1])"/>
 
 <!--Process Level 1 Lists-->
 <xsl:template match="kom[@eb='1' and @ty='aufz']" mode="startlist_ul">
	 <xsl:apply-templates select="." mode="Items_ul"/>
	 <xsl:apply-templates select="key('sublistitems2_ul', generate-id())"
	 mode="openlist_ul"/>
	 <xsl:apply-templates select="key('listitems1_ul', generate-id())"
	 mode="startlist_ul"/>
 </xsl:template>
 
 <!--Process Level 2 Lists-->
 <xsl:template match="kom[@eb='2' and @ty='aufz']" mode="startlist_ul">
	  <xsl:apply-templates select="." mode="Items_ul"/>
	  <xsl:apply-templates select="key('sublistitems3_ul', generate-id())"
	 mode="openlist_ul"/>
	  <xsl:apply-templates select="key('listitems2_ul', generate-id())"
	 mode="startlist_ul"/>
 </xsl:template>
 
 <!--Process Level 3 Lists-->
 <xsl:template match="kom[@eb='3' and @ty='aufz']" mode="startlist_ul">
	  <xsl:apply-templates select="." mode="Items_ul"/>
	  <xsl:apply-templates select="key('listitems3_ul', generate-id())"
	 mode="startlist_ul"/>
 </xsl:template>
  
 <!--Start a list if the contents is not empty-->
 <xsl:template match="kom[(@eb='1' or @eb='2' or @eb='3') and @ty='aufz']" mode="openlist_ul">
	 <xsl:if test="string(.)!=''">
	 
	 <ul>
 		<xsl:if test="@code">
			<xsl:attribute name="code"><xsl:value-of select="@code"/></xsl:attribute>
		</xsl:if>
		<xsl:if test="@type">
			<xsl:attribute name="type"><xsl:value-of select="@type"/></xsl:attribute>
		</xsl:if>
	<xsl:apply-templates select="." mode="startlist_ul"/>
	</ul>
	
	</xsl:if>
 </xsl:template>
 
 <!--Output Individual List Items-->
 <xsl:template match="kom[(@eb='1' or @eb='2' or @eb='3') and @ty='aufz']" mode="Items_ul">
	<xsl:if test="string(.)!=''">
		<li>
			<xsl:copy-of select="node()"/>
		</li>
	</xsl:if>
 </xsl:template>
  
 <!--Pick out the first level 1 item in a list, dump other items -->
 <xsl:template match="kom[(@eb='1' or @eb='2' or @eb='3') and @ty='aufz']" >
	<xsl:if test="@eb='1' and @ty='aufz' and not (preceding-sibling::kom[1][@eb='1' or @eb='2' or @eb='3'])">
	<ul>
  		<xsl:if test="@code">
			<xsl:attribute name="code"><xsl:value-of select="@code"/></xsl:attribute>
		</xsl:if>
		<xsl:if test="@type">
			<xsl:attribute name="type"><xsl:value-of select="@type"/></xsl:attribute>
		</xsl:if>
	<xsl:apply-templates select="." mode="startlist_ul"/>
	</ul>
	</xsl:if>
 </xsl:template>
.....

Das ist der derzeitige Output:
HTML:
<p class="content">irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></p>
<ul>
	<li>Das ist der erste Punkt</li>
	<li>Das ist der zweite Punkt</li>
	<ul>
		<li>Das ist der erste Punkt der <f>zweiten</f> Ebene</li>
			<ul>
				<li>Das ist der erste Punkt der <f>dritten</f> Ebene</li>
			</ul>
	</ul>
	<li>Das ist der dritte Punkt</li>
	<li>Das ist der vierte Punkt</li>
</ul>
<p class="content">irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></p>

Dieser ist leider nicht xhtml transitional konform, weil zB das zweite li Element geschlossen wird.

Kann mir jemand sagen, was ich an dem Beispiel andern muss um xhtml transitional Konformität zu erhalten?
Danke.
Halmarius
 
Hallo,

ich habe dein XML etwas schlanker gemacht und angenommen es sieht so aus:
XML:
<root>
	<item level="1">Punkt 1</item>
	<item level="1">Punkt 2</item>
	<item level="2">Punkt 2.1</item>
	<item level="3">Punkt 2.1.1</item>
	<item level="4">Punkt 2.1.1.1</item>
	<item level="3">Punkt 2.1.2</item>
	<item level="2">Punkt 2.2</item>
	<item level="3">Punkt 2.2.1</item>
	<item level="2">Punkt 2.3</item>
	<item level="1">Punkt 3</item>
	<item level="1">Punkt 4</item>
</root>
Zu jedem item-Knoten kannst du mit einem entsprechenden Schlüssel die folgenden Knoten mit tieferer Ebene finden. Wenn also ein Knoten Folgeknoten mit tieferer Ebene hat, dann öffnest du ein neues UL-Element innerhalb des aktuellen LI-Elements:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

	<xsl:output method="html"
		doctype-system="about:legacy-compat"
		media-type="text/html"
		encoding="UTF-8"
		omit-xml-declaration="yes"
		indent="yes"
	/>

	<!-- Nachfolgende item-Knoten in tieferer Ebene finden -->
	<xsl:key
		name="next-levels"
		match="item[@level='4']"
		use="generate-id(preceding-sibling::item[@level='1' or @level='2' or @level='3'][1])" />
	<xsl:key
		name="next-levels"
		match="item[@level='3']"
		use="generate-id(preceding-sibling::item[@level='1' or @level='2'][1])" />
	<xsl:key
		name="next-levels"
		match="item[@level='2']"
		use="generate-id(preceding-sibling::item[@level='1'][1])" />

	<xsl:template match="/root">
		<html>
			<head>
				<title>Test</title>
			</head>
			<body>
				<ul>
					<!-- item-Knoten der obersten Ebene übergeben: -->
					<xsl:apply-templates select="item[@level='1']" />
				</ul>
			</body>
		</html>
	</xsl:template>

	<xsl:template match="item">
		<li>
			<xsl:value-of select="." />
			<!-- Gibt es nachfolgende Knoten in tieferer Ebene? -->
			<xsl:if test="count(key('next-levels',generate-id()))">
				<ul>
					<!-- Nachfolgende Knoten in tieferer Ebene übergeben: -->
					<xsl:apply-templates select="key('next-levels',generate-id())" />
				</ul>
			</xsl:if>
		</li>
	</xsl:template>

</xsl:stylesheet>
Das war's schon. :)
 
Zuletzt bearbeitet von einem Moderator:
Danke für das Beispiel. Ich hab versucht es für meine Bedingungen anzupassen:

HTML:
    <xsl:key
        name="next-levels"
        match="kom[@ty='aufz' and @eb='3']"
        use="generate-id(preceding-sibling::kom[(@ty='aufz' and @eb='1') or (@ty='aufz' and @eb='2')][1])" />
    <xsl:key
        name="next-levels"
        match="kom[@ty='aufz' and @eb='2']"
        use="generate-id(preceding-sibling::kom[@ty='aufz' and @eb='1'][1])" />
 
	<xsl:template match="kom[@ty='aufz' and @eb='1']">
		<ul>
			<xsl:apply-templates select="kom[@ty='aufz' and @eb='1']" />
		</ul>
	</xsl:template>
 
	<xsl:template match="kom[@ty='aufz' and @eb>0]" >
			<li>
				<xsl:value-of select="." />
				<xsl:if test="count(key('next-levels',generate-id()))">
					<ul>
						<xsl:apply-templates select="key('next-levels',generate-id())" />
					</ul>
				</xsl:if>
			</li>
	</xsl:template>

Als Ergebnis kam die Meldung:
HTML:
Ambiguous rule match for...
Was mir auch nachvollziehbar ist.

Das Ergebnis war wie folgt:

HTML:
<p class="content">irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></p>
<li>Das ist der erste Punkt</li>
<li>Das ist der zweite Punkt
	<ul>
		<li>Das ist der erste Punkt der zweiten Ebene</li>
	</ul>
</li>
<li>Das ist der erste Punkt der zweiten Ebene</li>
<li>Das ist der dritte Punkt</li>
<li>Das ist der vierte Punkt</li>
<p class="content">irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></p>

Was mir darin auffällt:
  • die f-Elementnamen innerhalb der li-Elemente fehlen und
  • der erste Punkt der zweiten Ebene ist doppelt vorhanden

Bin für jeden Hinweis dankbar.

Grüße
Halmarius
 
Hallo,

wenn du in den KOM-Knoten noch Kindelemente hast, die du unverändert weiterverwenden möchtest, dann brauchst du nur den Knoteninhalt mit dem COPY-OF-Element als Inhalt des LI-Elements in den Ausgabebaum kopieren.
Statt
XML:
<xsl:value-of select="." />
müsstest du dann folgendes notieren
XML:
<xsl:copy-of select="node()" />

Ansonsten müsste man ein "Identity Copy" verwenden, um z.B. den Elementnamen zu ändern:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="xml" indent="yes" encoding="UTF-8" />

  <xsl:key
    name="next-levels"
    match="kom[@ty='aufz' and @eb='3']"
    use="generate-id(preceding-sibling::kom[@ty='aufz' and (@eb='1' or @eb='2')][1])" />
  <xsl:key
    name="next-levels"
    match="kom[@ty='aufz' and @eb='2']"
    use="generate-id(preceding-sibling::kom[@ty='aufz' and @eb='1'][1])" />
 
  <xsl:template match="/root">
    <ul>
      <xsl:apply-templates select="kom[@ty='aufz' and @eb='1']" />
    </ul>
  </xsl:template>
 
  <xsl:template match="kom" >
    <li>
      <xsl:apply-templates select="node()" mode="icopy" />
      <xsl:if test="key('next-levels',generate-id())">
        <ul>
          <xsl:apply-templates select="key('next-levels',generate-id())" />
        </ul>
      </xsl:if>
    </li>
  </xsl:template>

  <xsl:template match="@*|node()" mode="icopy">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" mode="icopy" />
    </xsl:copy>  
  </xsl:template>

  <xsl:template match="f" mode="icopy">
    <b>
      <xsl:apply-templates select="@*|node()" mode="icopy" />
    </b>
  </xsl:template>

</xsl:stylesheet>
Damit kann ich das XML-Quelldokument
XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
		<kom>irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></kom>
		<kom ty="aufz" eb="1">Das ist der erste Punkt</kom>
		<kom ty="aufz" eb="1">Das ist der zweite Punkt</kom>
		<kom ty="aufz" eb="2">Das ist der erste Punkt der <f class="fett">zweiten</f> Ebene</kom>
		<kom ty="aufz" eb="3">Das ist der erste Punkt der <f class="fett">dritten</f> Ebene</kom>
		<kom ty="aufz" eb="1">Das ist der dritte Punkt: H<sub>2</sub>SO<sub>4</sub></kom>
		<kom ty="aufz" eb="1">Das ist der vierte Punkt: E = m·c<sup>2</sup></kom>
		<kom ty="oNr">irgend ein text <f>fetter text</f> irgend ein text <k>kursiver text</k></kom>
</root>
... in diese Ausgabe transformieren:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<ul>
  <li>Das ist der erste Punkt</li>
  <li>Das ist der zweite Punkt
    <ul>
      <li>Das ist der erste Punkt der <b class="fett">zweiten</b> Ebene
        <ul>
          <li>Das ist der erste Punkt der <b class="fett">dritten</b> Ebene</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Das ist der dritte Punkt: H<sub>2</sub>SO<sub>4</sub></li>
  <li>Das ist der vierte Punkt: E = m·c<sup>2</sup></li>
</ul>
Ich habe das mit "libxslt" transformiert, was anderes steht mir im Augenblick nicht zur Verfügung.
 
Zuletzt bearbeitet von einem Moderator:
Hallo,

Danke für den Beispiel-Code und die Hinweise.
Hab es bei mir für das kleine Beispiel ausprobiert und es hat funktioniert.
Dieses kleine Beispiel ist allerdings nur ein kleiner Ausschnitte aus komplexeren xml Daten. Sobald ich den Beispiel-Code darauf anwendet funkt es nicht mehr. Hat offenbar etwas mit dem "Einstiegspunkt" zu tun.
Wir verwenden derzeit ein anderes xsl Script (Kurzfassung des Scripts im Anhang), welches aber mit der Konvertierung von Listen nichts am Hut hat. Leider ist es mir nicht gelungen den Beispiel-Code auf die Schnelle in dieses xsl Script zu intergrieren.
Beispiel-Daten sind ebenfalls in Anhang.
Wir verwenden derzeit den saxon9.
Bin für jeden Hinweis dankbar.
Danke.

Grüße
Halmarius
 

Anhänge

  • mapping_und_daten.zip
    1,4 KB · Aufrufe: 14
Zurück