Announcement

Collapse
No announcement yet.

Teile eines XML auslesen (komplex)

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

  • #16
    Mit XSL 2.0 geht die funktion jetzt, aber dein Code liefert nicht das gewünschte Resultat.

    Hier das XSL:
    Code:
     <xsl:param name="start" select="'THIS'"/>
      
      <xsl:output method="xml" indent="yes"/>
      <xsl:strip-space elements="*"/>
      
      <xsl:key name="k1" match="*[@Name]" use="@Name"/>
      
      <xsl:template match="/">
        <xsl:variable name="rtf-references">
          <xsl:apply-templates select="//Cat[@Name = $start]"/>
        </xsl:variable>
        <xsl:for-each select="  $rtf-references/*[generate-id() = generate-id(key('k1', @Name)[2] )]"> 
              <xsl:copy-of select="."/>
        </xsl:for-each>
      </xsl:template>
      
      <xsl:template match="*[@Name]">
        <xsl:copy-of select="."/>
        
        <xsl:apply-templates select="descendant::*[starts-with(local-name(), 'p')]" />
      </xsl:template>
      
      <xsl:template match="*[@Name]/*[starts-with(local-name(), 'p')]">
      <xsl:if test="not(preceding::*[@Name=local-name()])">
        <xsl:apply-templates select="key('k1', .)"/>
        </xsl:if>
      </xsl:template>
    Der Output ist
    Code:
    <Cat Name="THIS">
    	<a>egal</a>
    	<b>egal</b>
    	<pA>Text1</pA>
    	<pB>Text2</pB>
    	<aaa>
    		<dd>
    			<pC>Text3</pC>
    		</dd>
    	</aaa>
    </Cat>
    <hier1 Name="Text1">
    	<u1>dies1</u1>
    	<u2>dies2</u2>
    	<pD>Text4</pD>
    </hier1>
    <hier4 Name="Text4">
    	<pE>Text5</pE>
    </hier4>
    <hier5 Name="Text5">
    	<u1>dies1</u1>
    	<u2>dies2</u2>
    </hier5>
    <hier2 Name="Text2" Co="ssss">
    	<u1>dies1&gt;</u1>
    	<u2>dies2&gt;</u2>
    	<pG>Text4</pG>
    </hier2>
    <hier4 Name="Text4">
    	<pE>Text5</pE>
    </hier4>
    <hier5 Name="Text5">
    	<u1>dies1</u1>
    	<u2>dies2</u2>
    </hier5>
    D.h. Text4 und Text5 erscheinen dennoch doppelt und Text3 wird gar nicht erst
    gematcht.

    Könntest du noch mal drüber schauen?

    Danke!

    Comment


    • #17
      Mein XSLT 1.0 Vorschlag steht in http://entwickler-forum.de/showpost....3&postcount=11, wenn man den exsl:node-set Aufruf heraus nimmt, sieht der Code folgendermassen aus:
      Code:
      <xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0">
        
        <xsl:param name="start" select="'THIS'"/>
        
        <xsl:output method="xml" indent="yes"/>
        <xsl:strip-space elements="*"/>
        
        <xsl:key name="k1" match="*[@Name]" use="@Name"/>
        
        <xsl:template match="/">
          <xsl:variable name="rtf-references">
            <xsl:apply-templates select="//Cat[@Name = $start]"/>
          </xsl:variable>
          <xsl:for-each select="$rtf-references/*[generate-id() = generate-id(key('k1', @Name)[1])]">
            <xsl:copy-of select="."/>
          </xsl:for-each>
        </xsl:template>
        
        <xsl:template match="*[@Name]">
          <xsl:copy-of select="."/>
          <xsl:apply-templates select="*[starts-with(local-name(), 'p')]" />
        </xsl:template>
        
        <xsl:template match="*[@Name]/*[starts-with(local-name(), 'p')]">
          <xsl:apply-templates select="key('k1', .)"/>
        </xsl:template>
      
      </xsl:stylesheet>
      Warum du daran diverse Änderungen vornimmst, aber dann "dein Code liefert nicht das gewünschte Resultat" schreibst, verstehe ich nicht.

      Wenn du statt der Kindselemente alle Nachfahren verarbeiten willst, kannst du das ändern, aber warum du den Index im Prädikat auf 2 änderst und den key-Aufruf heraus nimmst, verstehe ich nicht.

      Wenn man sowohl das apply-templates als auch das match-Attribut ändert, sieht das Stylesheet folgendermassen aus:
      Code:
      <xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0">
        
        <xsl:param name="start" select="'THIS'"/>
        
        <xsl:output method="xml" indent="yes"/>
        <xsl:strip-space elements="*"/>
        
        <xsl:key name="k1" match="*[@Name]" use="@Name"/>
        
        <xsl:template match="/">
          <xsl:variable name="rtf-references">
            <xsl:apply-templates select="//Cat[@Name = $start]"/>
          </xsl:variable>
          <xsl:for-each select="$rtf-references/*[generate-id() = generate-id(key('k1', @Name)[1])]">
            <xsl:copy-of select="."/>
          </xsl:for-each>
        </xsl:template>
        
        <xsl:template match="*[@Name]">
          <xsl:copy-of select="."/>
          <xsl:apply-templates select="descendant::*[starts-with(local-name(), 'p')]" />
        </xsl:template>
        
        <xsl:template match="*[@Name]//*[starts-with(local-name(), 'p')]">
          <xsl:apply-templates select="key('k1', .)"/>
        </xsl:template>
      
      </xsl:stylesheet>
      Wenn ich das auf die Eingabedatei von
      Code:
      <Root>
      		<Group>
      			<Cat Name="A">
      				<a>egal</a>
      				<b>egal</b>
      				<pA>egal</pA>
      				<pB>egal</pB>
      			</Cat>
      			<Cat Name="THIS">
      				<a>egal</a>
      				<b>egal</b>
      				<pA>Text1</pA>
      				<pB>Text2</pB>
      				<aaa>
      					<dd>
      						<pC>Text3</pC>
      					</dd>
      				</aaa>
      			</Cat>
      		</Group>
      		<Group>
      			<not11>
      		</not11>
      			<hier4 Name="Text4">
      			<pE>Text5</pE>
      			</hier4>
      		</Group>
      		<Group>
      			<not12/>
      			<hier5>
      				<pF>Text3</pF>
      			</hier5>
      		</Group>
      		<Group>
      			<hier5 Name="Text5">
      				<u1>dies1</u1>
      				<u2>dies2</u2>
      			</hier5>
      		</Group>
      		<Group>
      			<hier1 Name="nein">
      				<u1>dies1</u1>
      				<u2>dies2</u2>
      			</hier1>
      			<hier1 Name="Text1">
      				<u1>dies1</u1>
      				<u2>dies2</u2>
      				<pD>Text4</pD>
      			</hier1>
      			<not1 Name="NO">
      				<gh>nein</gh>
      			</not1>
      			<not2>
      				<not3>
      					<hier2 Name="Text2" Co="ssss">
      						<u1>dies1></u1>
      						<u2>dies2></u2>
      						<pG>Text4</pG>
      					</hier2>
      				</not3>
      			</not2>
      		</Group>
      		<Group>
      			<not4>
      				<not5>
      					<hier3 Name="Text3">
      						<u1>dies1></u1>
      						<!-- hier könnten weitere 'p' tags stehden die dann evtl. auch wieder 'p'Tags haben-->
      					</hier3>
      				</not5>
      			</not4>
      		</Group>
      	</Root>
      anwende, erhalte ich
      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <Cat Name="THIS">
         <a>egal</a>
         <b>egal</b>
         <pA>Text1</pA>
         <pB>Text2</pB>
         <aaa>
            <dd>
               <pC>Text3</pC>
            </dd>
         </aaa>
      </Cat>
      <hier1 Name="Text1">
         <u1>dies1</u1>
         <u2>dies2</u2>
         <pD>Text4</pD>
      </hier1>
      <hier4 Name="Text4">
         <pE>Text5</pE>
      </hier4>
      <hier5 Name="Text5">
         <u1>dies1</u1>
         <u2>dies2</u2>
      </hier5>
      <hier2 Name="Text2" Co="ssss">
         <u1>dies1&gt;</u1>
         <u2>dies2&gt;</u2>
         <pG>Text4</pG>
      </hier2>
      <hier3 Name="Text3">
         <u1>dies1&gt;</u1>
         <!-- hier könnten weitere 'p' tags stehden die dann evtl. auch wieder 'p'Tags haben--></hier3>
      Da sind alle Querverweise berücksichtigt, aber Duplikate entfernt.

      Comment


      • #18
        Und wenn ohnehin XSLT 2.0 zum Einsatz kommt, kann man die Duplikate besser mit for-each-group entfernen:
        Code:
        <xsl:stylesheet
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          version="2.0">
          
          <xsl:param name="start" select="'THIS'"/>
          
          <xsl:output method="xml" indent="yes"/>
          <xsl:strip-space elements="*"/>
          
          <xsl:key name="k1" match="*[@Name]" use="@Name"/>  
          
          <xsl:template match="/">
            <xsl:variable name="cross-references">
              <xsl:apply-templates select="//Cat[@Name = $start]"/>
            </xsl:variable>
            <xsl:for-each-group select="$cross-references/*" group-by="@Name">
              <xsl:copy-of select="."/>
            </xsl:for-each-group>
          </xsl:template>
          
          <xsl:template match="*[@Name]">
            <xsl:copy-of select="."/>
            <xsl:apply-templates select="descendant::*[starts-with(local-name(), 'p')]" />
          </xsl:template>
          
          <xsl:template match="*[@Name]//*[starts-with(local-name(), 'p')]">
            <xsl:apply-templates select="key('k1', .)"/>
          </xsl:template>
        
        </xsl:stylesheet>

        Comment


        • #19
          Danke Martin,
          der zuletzt von dir eingestellte XSLT 2.0 Code funktioniert exakt.

          Leider kam inzwischen - von ganz oben - die Restriktion, dass es XSL 1.0 OHNE EXSL sein muss.
          Ich bastele gerade an dem Versuch einer Transformation in reines 1.0 nach der Munchian Methode.

          Hast Du da einen Spontanvorschlag oder eine bessere Idee?

          Erschwerend kam wohl noch hinzu, dass Endloschleifen innerhalb der gefundenen <p..> möglich sind.
          Hier daher der neue Testcode mit Endlosschleife:
          Code:
          <?xml version="1.0" encoding="UTF-8"?>
          <Root>
          		<Group>
          			<Cat Name="A">
          				<a>egal</a>
          				<b>egal</b>
          					<hier4 Name="Text4">
          			<pE>Text1</pE>
          			</hier4>
          				<pA>egal</pA>
          				<pB>egal</pB>
          			</Cat>
          			<Cat Name="THIS">
          				<a>egal</a>
          				<b>egal</b>
          				<pA>Text1</pA>
          				<pB>Text2</pB>
          				<aaa>
          					<dd>
          						<pC>Text3</pC>
          					</dd>
          				</aaa>
          			</Cat>
          		</Group>
          		<Group>
          			<not11>
          		</not11>
          			
          		</Group>
          		<Group>
          			<not12/>
          			<hier5>
          				<pF>Text3</pF>
          			</hier5>
          		</Group>
          		<Group>
          			<hier5 Name="Text5">
          				<u1>dies1</u1>
          				<u2>dies2</u2>
          			</hier5>
          		</Group>
          		<Group>
          			<hier1 Name="nein">
          				<u1>dies1</u1>
          				<u2>dies2</u2>
          			</hier1>
          			<hier1 Name="Text1">
          				<u1>dies1</u1>
          				<u2>dies2</u2>
          				<pD>Text4</pD>
          			</hier1>
          			<not1 Name="NO">
          				<gh>nein</gh>
          			</not1>
          			<not2>
          				<not3>
          					<hier2 Name="Text2" Co="ssss">
          						<u1>dies1></u1>
          						<u2>dies2></u2>
          						<pG>Text4</pG>
          					</hier2>
          				</not3>
          			</not2>
          		</Group>
          		<Group>
          			<not4>
          				<not5>
          					<hier3 Name="Text3">
          						<u1>dies1></u1>
          						<!-- hier könnten weitere 'p' tags stehden die dann evtl. auch wieder 'p'Tags haben-->
          					</hier3>
          				</not5>
          			</not4>
          		</Group>
          	</Root>
          Übrigens an dieser Stelle, wenn auch eigentlich VIEL zu spät:
          vielen Dank für Deine perfekte und unermüdliche Hilfe

          Gruß
          Ferry

          Comment


          • #20
            Über XSLT 1.0 ohne exsl:node-set (oder vergleichbares) möchte ich ehrlich gesagt nicht nachdenken, es sei denn, man kann zwei Stylesheets schreiben, beim dem das erste die Querverweise verfolgt und ausgibt und dann das zweite per Muenchian grouping die Duplikate entfernt.

            Hier ist mal eine Adaption der XSLT 2.0 Lösung, die auch bei Schleifen in den Querverweisen funktioniert:
            Code:
            <xsl:stylesheet
              xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema"
              exclude-result-prefixes="xsd"
              version="2.0">
              
              <xsl:param name="start" select="'THIS'"/>
              
              <xsl:output method="xml" indent="yes"/>
              <xsl:strip-space elements="*"/>
              
              <xsl:key name="k1" match="*[@Name]" use="@Name"/>  
              
              <xsl:template match="/">
                <xsl:variable name="cross-references">
                  <xsl:apply-templates select="//Cat[@Name = $start]"/>
                </xsl:variable>
                <xsl:for-each-group select="$cross-references/*" group-by="@Name">
                  <xsl:copy-of select="."/>
                </xsl:for-each-group>
              </xsl:template>
              
              <xsl:template match="*[@Name]">
                <xsl:param name="processed" as="xsd:string*" select="()"/>
                <xsl:copy-of select="."/>
                <xsl:apply-templates select="descendant::*[starts-with(local-name(), 'p')]">
                  <xsl:with-param name="processed" select="$processed, generate-id(current())"/>
                </xsl:apply-templates>
              </xsl:template>
              
              <xsl:template match="*[@Name]//*[starts-with(local-name(), 'p')]">
                <xsl:param name="processed" as="xsd:string*"/>
                <xsl:apply-templates select="key('k1', .)[not(generate-id() = $processed)]">
                  <xsl:with-param name="processed" select="$processed"/>
                </xsl:apply-templates>
              </xsl:template>
            
            </xsl:stylesheet>
            Sich die generate-id() der schon verarbeiteten Elemente zu merken, müsste man auch in eine XSLT 1.0 Lösung einbauen, aber das werde ich heute nicht mehr versuchen.

            Comment


            • #21
              Bin inzwischen zu müde, um noch weiter zu denken oder auch nur deinen neuen Code näher zu inspizieren.
              Daher hier und jetzt nur ein schnelles Feedback:

              es ist völlig OK zwei XSl Dateien nacheinander auf das XML anzuwenden.

              Hilft das?

              Comment


              • #22
                Hier ist erst mal eine XSLT 1.0 Lösung, die exsl:node-set benötigt, aber dafür mit einem Stylesheet alle Aufgaben (Verfolgen der Querverweise, aber keine endlose Rekursion bei Zyklen in den Verweisen, Elemenieren von Duplikaten) erledigt:
                Code:
                <xsl:stylesheet
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  xmlns:exsl="http://exslt.org/common"
                  exclude-result-prefixes="exsl"
                  version="1.0">
                  
                  <xsl:param name="start" select="'THIS'"/>
                  
                  <xsl:output method="xml" indent="yes"/>
                  <xsl:strip-space elements="*"/>
                  
                  <xsl:key name="k1" match="*[@Name]" use="@Name"/>
                  
                  <xsl:template match="/">
                    <xsl:variable name="rtf-references">
                      <xsl:apply-templates select="key('k1', $start)"/>
                    </xsl:variable>
                    <xsl:for-each select="exsl:node-set($rtf-references)/*[generate-id() = generate-id(key('k1', @Name)[1])]">
                      <xsl:copy-of select="."/>
                    </xsl:for-each>
                  </xsl:template>
                  
                  <xsl:template match="*[@Name]">
                    <xsl:param name="processed" select="'|'"/>
                    <xsl:copy-of select="."/>
                    <xsl:apply-templates select="descendant::*[starts-with(local-name(), 'p')]">
                      <xsl:with-param name="processed" select="concat($processed, generate-id(current()), '|')"/>
                    </xsl:apply-templates>
                  </xsl:template>
                  
                  <xsl:template match="*[@Name]//*[starts-with(local-name(), 'p')]">
                    <xsl:param name="processed"/>
                    <xsl:apply-templates select="key('k1', .)[not(contains($processed, generate-id()))]">
                      <xsl:with-param name="processed" select="$processed"/>
                    </xsl:apply-templates>
                  </xsl:template>
                
                </xsl:stylesheet>
                Wenn wirklich kein exsl:node-set oder vergleichbares zur Verfügung steht, dann muss man zwei Stylesheets benutzen, bei dem das zweite Stylesheet die Ausgabe des ersten verarbeitet:
                Code:
                <xsl:stylesheet
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  version="1.0">
                  
                  <xsl:param name="start" select="'THIS'"/>
                  
                  <xsl:output method="xml" indent="yes"/>
                  <xsl:strip-space elements="*"/>
                  
                  <xsl:key name="k1" match="*[@Name]" use="@Name"/>
                  
                  <xsl:template match="/">
                    <temp-root>
                      <xsl:apply-templates select="key('k1', $start)"/>
                    </temp-root>
                  </xsl:template>
                  
                  <xsl:template match="*[@Name]">
                    <xsl:param name="processed" select="'|'"/>
                    <xsl:copy-of select="."/>
                    <xsl:apply-templates select="descendant::*[starts-with(local-name(), 'p')]">
                      <xsl:with-param name="processed" select="concat($processed, generate-id(current()), '|')"/>
                    </xsl:apply-templates>
                  </xsl:template>
                  
                  <xsl:template match="*[@Name]//*[starts-with(local-name(), 'p')]">
                    <xsl:param name="processed"/>
                    <xsl:apply-templates select="key('k1', .)[not(contains($processed, generate-id()))]">
                      <xsl:with-param name="processed" select="$processed"/>
                    </xsl:apply-templates>
                  </xsl:template>
                
                </xsl:stylesheet>
                Code:
                <xsl:stylesheet
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  version="1.0">
                
                  <xsl:output method="xml" indent="yes"/>
                  <xsl:strip-space elements="*"/>
                  
                  <xsl:key name="k1" match="*[@Name]" use="@Name"/>
                  
                  <xsl:template match="/">
                    <xsl:for-each select="temp-root/*[generate-id() = generate-id(key('k1', @Name)[1])]">
                      <xsl:copy-of select="."/>
                    </xsl:for-each>
                  </xsl:template>
                
                </xsl:stylesheet>

                Comment

                Working...
                X