Announcement

Collapse
No announcement yet.

Problem mit einer XML Transformation (Stichwort transponieren)

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Problem mit einer XML Transformation (Stichwort transponieren)

    Hallo zusammen,

    erst einmal großes Lob an dieses Forum. Habt mir schon oft aus der Patsche geholfen. Jedoch hab ich diesmal ein konkretes Problem zu dem ich hier keine Lösung gefunden habe. Dies ist mein erster Beitrag, hoffe ihr könnt mir helfen.

    Ausgangssituation:

    Folgende XML kann ich erfolgreich in mein gewünschtes Zielformat mittels XSLT umwandeln.
    Code:
    <?xml version="1.0" encoding="UTF.8"?>
    <roottag>
    	<test name="#1">
    		<data>
    			<r>
    				<c>2012.01.01 00:15</c>
    				<c>1</c>				
    			</r>
    			<r>
    				<c>2012.01.01 00:30</c>
    				<c>2</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:00</c>
    				<c>3</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:15</c>
    				<c>4</c>				
    			</r>
    		</data>
    	</test>
    	<test name="#2">
    		<data>
    			<r>
    				<c>2012.01.01 00:15</c>
    				<c>5</c>				
    			</r>
    			<r>
    				<c>2012.01.01 00:30</c>
    				<c>6</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:00</c>
    				<c>7</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:15</c>
    				<c>8</c>				
    			</r>
    		</data>
    	</test>
    	<test name="#3">
    		<data>
    			<r>
    				<c>2012.01.01 00:15</c>
    				<c>9</c>				
    			</r>
    			<r>
    				<c>2012.01.01 00:30</c>
    				<c>10</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:00</c>
    				<c>11</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:15</c>
    				<c>12</c>				
    			</r>
    		</data>
    	</test>
    </roottag>
    Hier die zugehörige XSLT

    Code:
    
    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes"/>
    
     <xsl:variable name="vNumRows" select="count(/*/test)"/>
     <xsl:variable name="vNumCols" select="count(/*/test[1]/data/r)"/>
     <xsl:template match="roottag"><xsl:text>&#x9;&#x9;&#x9;&#x9;</xsl:text><xsl:for-each select="/roottag/test">
     <xsl:variable name="vColPos1" select="position()"/><xsl:value-of select="/roottag/test[$vColPos1]/@name"/><xsl:if test="$vColPos1!=$vNumRows" ><xsl:text>&#x9;</xsl:text></xsl:if></xsl:for-each>
    <xsl:text>&#xA;</xsl:text>
         <xsl:for-each select="test[1]/data/r">
          <xsl:variable name="vRowPos" select="position()"/>
           <xsl:for-each select="/roottag/test">
            <xsl:variable name="vColPos" select="position()"/>	
             <xsl:if test="$vColPos=1"><xsl:value-of select="/roottag/test[$vColPos]/data/r[$vRowPos]/c[1]"/></xsl:if><xsl:text>&#x9;</xsl:text><xsl:value-of select="/roottag/test[$vColPos]/data/r[$vRowPos]/c[2]"/><xsl:if test="$vColPos=$vNumRows" ><xsl:text>&#xA;</xsl:text></xsl:if>
    </xsl:for-each>
    </xsl:for-each>
    </xsl:template>
    </xsl:stylesheet>
    Als Resultat bekomme ich folgendes:
    Code:
    				#1	#2	#3
    2012.01.01 00:15		1	5	9
    2012.01.01 00:30		2	6	10
    2012.01.01 01:00		3	7	11
    2012.01.01 01:15		4	8	12

    Soweit so gut, jedoch haben sich nun die Vorraussetzungen geändert. Jetzt muss ich auch folgende XML nach Datum sortiert schön verteilt ausgeben.

    Hier die XML:

    Code:
    <?xml version="1.0" encoding="UTF.8"?>
    <roottag>
    	<test name="#1">
    		<data>
    			<r>
    				<c>2012.01.01 00:15</c>
    				<c>1</c>				
    			</r>
    			<r>
    				<c>2012.01.01 00:30</c>
    				<c>2</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:00</c>
    				<c>3</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:15</c>
    				<c>4</c>				
    			</r>
    		</data>
    	</test>
    	<test name="#2">
    		<data>
    			<r>
    				<c>2012.01.01 00:15</c>
    				<c>5</c>				
    			</r>
    			<r>
    				<c>2012.01.01 00:30</c>
    				<c>6</c>				
    			</r>
    			<r>
    				<c>2012.01.01 00:45</c>
    				<c>7</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:00</c>
    				<c>8</c>				
    			</r>
    			<r>
    				<c>2012.01.01 01:15</c>
    				<c>9</c>				
    			</r>
    		</data>
    	</test>
    	<test name="#3">
    		<data>
    			<r>
    				<c>2012.01.01 02:15</c>
    				<c>10</c>				
    			</r>
    			<r>
    				<c>2012.01.01 02:30</c>
    				<c>11</c>				
    			</r>
    		</data>
    	</test>
    </roottag>

    Jetzt muss das Resultat folgendermaßen aussehen:

    Code:
    				#1	#2	#3
    		
    2012.01.01 00:15		1	5
    2012.01.01 00:30		2	6
    2012.01.01 00:45			7	
    2012.01.01 01:00		3	8
    2012.01.01 01:15		4	9
    2012.01.01 02:15				10
    2012.01.01 02:30				11

    Bin sozusagen noch Anfänger, was XSLT angeht. Bin mir nicht sicher ob sich dieses Problem mittels XSLT überhaupt lösen lässt.

    Ich hoffe ihr könnt mir irgendwie weiterhelfen. Bin am verzweifeln.

    Vielen Dank jetzt schonmal

  • #2
    Probiere diesen XSLT-2.0-Ansatz (verarbeitet beide Varianten):
    [highlight=xml]<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"
    exclude-result-prefixes="fn xs">

    <xslutput method="xhtml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:variable name="datumzeit" select="fn:distinct-values(//r/c[1])" as="xs:string*"/>

    <xsl:template match="roottag">
    <html>
    <head>
    <title>Test</title>
    <style type="text/css">
    table,tr,td,th {border: 1px solid #000; border-collapse: collapse;}
    td,th{padding: 5px; text-align: center;}
    </style>
    </head>
    <body>
    <xsl:variable name="cur" select="current()"/>
    <table>
    <thead>
    <tr>
    <th>Datum/Zeit</th>
    <xsl:for-each select="$cur/test">
    <th><xsl:value-of select="@name"/></th>
    </xsl:for-each>
    </tr>
    </thead>
    <tbody>
    <xsl:for-each select="$datumzeit">
    <xsl:variable name="akt_dz" select="."/>
    <tr>
    <th><xsl:value-of select="$akt_dz"/></th>
    <xsl:for-each select="$cur/test/data">
    <xsl:variable name="c2" select="r[c[1] = $akt_dz]/c[2]"/>
    <td><xsl:value-of select="if(fn:string-length($c2) != 0) then $c2 else '-'"/></td>
    </xsl:for-each>
    </tr>
    </xsl:for-each>
    </tbody>
    </table>
    </body>
    </html>
    </xsl:template>

    </xsl:stylesheet>[/highlight]

    BTW: Zeichenkodierung lautet "UTF-8", nicht "UTF.8".

    Comment


    • #3
      Vielen Dank Thomas für den schnellen und pfiffigen Lösungsvorschlag. Leider hab ich vergessen zu erwähnen das die Transformation innerhalb eines C++ Projektes ausgeführt werden soll in dem ich als XSLT Prozessor den Xalan verwenden. Dieser unterstützt jedoch nur XSLT 1.0. Meines Wissens nach gibt es keine freie Bibliothek für C++, die XSLT 2.0 unterstützt. Daher sind mir insofern die Hände gebunden. Weiterhin muss das Ergebnis zwingend so aussehen, das nach Zeitstempeln sortiert wird. Hätte wohl die Umstände von Anfang an was präzisieren sollen. Vielleicht gibts ja doch ne Möglichkeit mit XSLT 1.0, das gewünschte Ergebnis zu erhalten.

      Comment


      • #4
        Für 1.0 mal statt fn:distinct-values mit Gruppierung nach Muench versuchen. Vielleicht lässt sich aus dem Programmkontext heraus Saxon-HE 9.4 in der .NET-Variante nutzen, machen Anwendungen wie FrameMaker ja auch. Zur Sortierung xsl:sort unterhalb von xsl:for-each mit $datumzeit einbauen, evtl. mit zusätzlichen Datumsfunktionen.

        Comment


        • #5
          Deine Tipp war echt super. Habs mal mit der Gruppierung nach Muench versucht, jedoch hab ich eigentlich nur noch ein Problem. Die Zuordnung zu den Spalten passt nicht mehr. In deinem XSLT 2.0 Beispiel konntest du schön für die Felder die keine Werte enthalten dein '---' einfügen. Bei der folgenden XSLT 1.0 Lösung komm ich im Moment gar nicht an die Information ob ich ein leeres Feld einfügen muss. Hier meine jetziger Stand

          Code:
            
          <xsl:stylesheet version="1.0"
           xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
           <xsl:output omit-xml-declaration="yes"/>
          <xsl:key name="datumzeit" match="r" use="c[1]" />
          		<xsl:template match="roottag"> <html>
                <head>
                  <title>Test</title>
                  <style type="text/css">
                    table,tr,td,th {border: 1px solid #000; border-collapse: collapse;}
                    td,th{padding: 5px; text-align: center;}
                  </style>
                </head>
                <body>
                  <xsl:variable name="cur" select="current()"/>
                  <table>
                    <thead>
                      <tr>
                        <th>Datum/Zeit</th>
                        <xsl:for-each select="$cur/test">
                          <th><xsl:value-of select="@name"/></th>
                        </xsl:for-each>
                      </tr>
                    </thead>
                    <tbody>
          	<xsl:for-each select="test/data/r[count(. | key('datumzeit', c[1])[1]) = 1]">
                           
                          <xsl:sort select="c[1]"/><tr><th><xsl:value-of select="c[1]" /></th>
          		<xsl:for-each select="key('datumzeit', c[1])">
          			<xsl:sort select="c[2]" />
          			<td><xsl:value-of select="c[2]" /></td>
          		</xsl:for-each></tr>
          	</xsl:for-each>
          	</tbody>  
                  </table>
                </body>
              </html>
          </xsl:template>
          </xsl:stylesheet>
          Hoffe es ist nur noch eine Kleinigkeit.

          Comment


          • #6
            Hoffe, so soll es sein:

            [highlight=xml]<?xml version="1.0" encoding="UTF-8"?>
            <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

            <xslutput method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
            <xsl:key name="datumzeit" match="r" use="c[1]"/>

            <xsl:template match="roottag">
            <html>
            <head>
            <title>Test</title>
            <style type="text/css">
            table,tr,td,th {border: 1px solid #000; border-collapse: collapse;}
            td,th{padding: 5px; text-align: center;}
            </style>
            </head>
            <body>
            <xsl:variable name="cur" select="current()"/>
            <table>
            <thead>
            <tr>
            <th>Datum/Zeit</th>
            <xsl:for-each select="$cur/test">
            <th>
            <xsl:value-of select="@name"/>
            </th>
            </xsl:for-each>
            </tr>
            </thead>
            <tbody>
            <xsl:for-each select="test/data/r[count(. | key('datumzeit', c[1])[1]) = 1]">
            <xsl:sort select="c[1]"/>
            <xsl:variable name="akt_dz" select="c[1]"/>
            <tr>
            <th>
            <xsl:value-of select="$akt_dz"/>
            </th>
            <xsl:for-each select="$cur/test/data">
            <xsl:variable name="c2" select="r[c[1] = $akt_dz]/c[2]"/>
            <td>
            <xsl:choose>
            <xsl:when test="string-length($c2) != 0">
            <xsl:value-of select="$c2"/>
            </xsl:when>
            <xsltherwise>
            <xsl:text>-</xsl:text>
            </xsltherwise>
            </xsl:choose>
            </td>
            </xsl:for-each>
            </tr>
            </xsl:for-each>
            </tbody>
            </table>
            </body>
            </html>
            </xsl:template>

            </xsl:stylesheet>[/highlight]

            Comment


            • #7
              Vielen Dank für die Hilfe. Ohne dich wäre ich aufgeschmissen. Hoffe ich kann mich mal revanchieren.

              Comment

              Working...
              X