Announcement

Collapse
No announcement yet.

Probleme mit Umstrukturierung eines XML-Files

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

  • Probleme mit Umstrukturierung eines XML-Files

    Hallo zusammen,

    leider habe ich mal wieder ein Xslt-Problem!
    Ich verwende XSLT 2.0 mit saxon9 HE.

    Ich würde gerne die nachfolgende Quell-XML umformen:
    Code:
    <Root>
    	<Paragraph>
    		<String>Unformatierter Text 1</String>
    		<String>Unformatierter Text 2</String>
    		<Font>
    			<FontStyle>kursiv</FontStyle>
    		</Font>
    		<String>Kursiver Text 1</String>	
    		<String>Kursiver Text 2</String>	
    		<Font>
    			<FontStyle></FontStyle>
    		</Font>
    		<String>Unformatierter Text</String>
    		<Font>
    			<FontStyle>fett</FontStyle>
    		</Font>
    		<String>fetter Text</String>	
    		<Font>
    			<FontStyle></FontStyle>
    		</Font>
    	</Paragraph>
    	<Paragraph>
    		<String>Unformatierter Text</String>
    	</Paragraph>
    </Root>
    Gewünschte Ziel-XML:
    Code:
    <Root>
    	<Paragraph>
    		<String>Unformatierter Text 1</String>
    		<String>Unformatierter Text 2</String>
    		<Text fontFormat="kursiv">
    			<String>Kursiver Text 1</String>	
    			<String>Kursiver Text 2</String>	
    		</Text>
    		<String>Unformatierter Text</String>
    		<Text fontFormat="fett">
    			<String>fetter Text</String>	
    		</Text>			
    	</Paragraph>
    	<Paragraph>
    		<String>Unformatierter Text</String>
    	</Paragraph>
    </Root>
    Die Quell-XML ist so zu verstehen:
    Es sind einzelne Paragraphen dargestellt, welche verschiede Strings enthalten.
    Diese Strings können unformatiert oder formatiert sein. Wenn ein String oder mehrere Strings formatiert sind, dann ist ein <Font> Element mit einem <FontStyle> (z.B. kursiv) das davor liegende Element. Die Formatierung endet sobald ein Element <Font> mit leerem <FontStyle> auftritt.
    In der Ziel-XML hätte ich gerne die formatierten Strings in einen <Text> mit entsprechendem Format als Attribut gepackt.

    Ich habe nun folgenden Ansatz gewählt:
    Code:
    <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      version="2.0">
    
      <xsl:template match="Root">
      	<xsl:for-each select="Paragraph">
    		<xsl:choose>
      			<xsl:when test="Font/FontStyle!=''">	
        			  <Text fontFormat="{FontStyle}">
    	   		</xsl:when>
          	                <xsl:when test="Font/FontStyle=''">			
      			  </Text>			
        		        </xsl:when>
                            <xsl:otherwise>
        			  <xsl:copy-of select="String"/>				
      			</xsl:otherwise>
    		</xsl:choose>
        </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>
    Leider bekomme ich nicht die gewünschte Ausgabe. Da verschiedene Fehler auftreten, wie z.B. dass das schließende <Text>-Tag fehlt.

    Hat jemand ne Idee was ich falsch mache. Bzw. wie ein richtiger Ansatz aussähe?

    Gruß
    Benni

  • #2
    Solche Probleme lassen sich in XSLT 2.0 mit for-each-group lösen, nimm dir mal die Zeit, die Beispiele unter http://www.w3.org/TR/xslt20/#grouping-examples zu lesen.
    Für dein Beispiel-Dokument reicht etwa for-each-group group-starting-with:
    Code:
    <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="2.0">
      
      <xsl:strip-space elements="*"/>
      <xsl:output indent="yes"/>
      
      <xsl:template match="Root">
        <xsl:copy>
          <xsl:apply-templates/>
        </xsl:copy>
      </xsl:template>
      
      <xsl:template match="Paragraph">
        <xsl:copy>
          <xsl:for-each-group select="*" group-starting-with="Font">
            <xsl:choose>
              <xsl:when test="self::Font[normalize-space(FontStyle)]">
                <Text fontFormat="{FontStyle}">
                  <xsl:copy-of select="current-group() except ."/>
                </Text>
              </xsl:when>
              <xsl:otherwise>
                <xsl:copy-of select="current-group()[not(self::Font[not(normalize-space(FontStyle))])]"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each-group>
        </xsl:copy>
      </xsl:template>
      
    </xsl:stylesheet>

    Comment


    • #3
      Vielen Dank!
      Durch die Beispiele wirds besser verständlich...

      Comment


      • #4
        Hey ihr,

        ahh das ist ja super, dass ich diesen Forumsbeitrag gefunden habe, da ich nämlich ein ähnliches Problem hatte bzw. habe. Martins Ansatz hatte mir erst mal weiter geholfen, aber aus den for-each-groups konnt ich bisher noch nicht so richtig schlau werden.

        Ich habe nun versucht mittels for-each-groups mein Problem (im Prinzip eine Erweiterung zu dem hier diskutierten Problem) selber zu lösen. Leider habe ich es bisher noch nicht geschafft...
        Bei mir sieht die Ausgangs-XML-Datei etwas anders aus (habe mal exemplarisch die hier verwendete einfach erweitert):

        Code:
        <Root>
        	<Paragraph>
        		<Line>
        			<String>Unformatierter Text 1</String>
        			<String>Unformatierter Text 2</String>
        			<Font>
        				<FontStyle>kursiv</FontStyle>
        			</Font>
        			<String>Kursiver Text 1</String>	
        			<String>Kursiver Text 2</String>	
        			<Font>
        				<FontStyle></FontStyle>
        			</Font>
        			<String>Unformatierter Text</String>
        			<Font>
        				<FontStyle>unterstrichen</FontStyle>
        			</Font>
        			<String>unterstrichener Text 1</String>	
        			<String>unterstrichener Text 2</String>	
        		</Line>	
        		<Line>
        			<String>unterstrichener Text 3</String>
        		<Line>
        	</Paragraph>
        	<Paragraph>
        		<String>Unformatierter Text</String>
        	</Paragraph>
        </Root>
        Hierbei soll der FontStyle Zeilen/Line-Übergreifend sein, d.h. gibt es nach einem
        FontStyle keinen weiteren Font-Tag, so gilt der vorherige FontStyle Zeilenübergreifend bis zum Paragraphenende (am obigen Beispiel für den "unterstrichen"-Text gut zu erkennen).
        Als Ergebnis bräuchte ich also sowas:

        Code:
        <Root>
        	<Paragraph>
        		<String>Unformatierter Text 1</String>
        		<String>Unformatierter Text 2</String>
        		<Text fontFormat="kursiv">
        			<String>Kursiver Text 1</String>	
        			<String>Kursiver Text 2</String>	
        		</Text>
        		<String>Unformatierter Text</String>
        		<Text fontFormat="fett">
        			<String>fetter Text</String>	
        		</Text>
        		<Text fontFormat="unterstrichen">
        			<String>unterstrichener Text 1</String>	
        			<String>unterstrichener Text 2</String>	
        			<String>unterstrichener Text 3</String>			
        		</Text>		
        	</Paragraph>
        	<Paragraph>
        		<String>Unformatierter Text</String>
        	</Paragraph>
        </Root>
        Hättet ihr mir hierfür eine Idee, wie ich das lösen kann?? Eigentlich
        dürfte ja zu dem was Martin gepostet hat nicht mehr viel fehlen!?

        Ich hoffe ihr könnt mir weiter helfen...

        Viele Grüße
        Melli

        Comment


        • #5
          Hier ist eine Adaption des geposteten Stylesheets, es wird nur der select-Ausdruck im for-each-group geändert:
          Code:
          <xsl:stylesheet
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            version="2.0">
            
            <xsl:strip-space elements="*"/>
            <xsl:output indent="yes"/>
            
            <xsl:template match="Root">
              <xsl:copy>
                <xsl:apply-templates/>
              </xsl:copy>
            </xsl:template>
            
            <xsl:template match="Paragraph">
              <xsl:copy>
                <xsl:for-each-group select="Line/* | *[not(self::Line)]" group-starting-with="Font">
                  <xsl:choose>
                    <xsl:when test="self::Font[normalize-space(FontStyle)]">
                      <Text fontFormat="{FontStyle}">
                        <xsl:copy-of select="current-group() except ."/>
                      </Text>
                    </xsl:when>
                    <xsl:otherwise>
                      <xsl:copy-of select="current-group()[not(self::Font[not(normalize-space(FontStyle))])]"/>
                    </xsl:otherwise>
                  </xsl:choose>
                </xsl:for-each-group>
              </xsl:copy>
            </xsl:template>
            
          </xsl:stylesheet>
          Damit macht Saxon 9.3 aus
          Code:
          <Root>
          	<Paragraph>
          		<Line>
          			<String>Unformatierter Text 1</String>
          			<String>Unformatierter Text 2</String>
          			<Font>
          				<FontStyle>kursiv</FontStyle>
          			</Font>
          			<String>Kursiver Text 1</String>	
          			<String>Kursiver Text 2</String>	
          			<Font>
          				<FontStyle></FontStyle>
          			</Font>
          			<String>Unformatierter Text</String>
          			<Font>
          				<FontStyle>unterstrichen</FontStyle>
          			</Font>
          			<String>unterstrichener Text 1</String>	
          			<String>unterstrichener Text 2</String>	
          		</Line>	
          		<Line>
          			<String>unterstrichener Text 3</String>
          		</Line>
          	</Paragraph>
          	<Paragraph>
          		<String>Unformatierter Text</String>
          	</Paragraph>
          </Root>
          dann
          Code:
          <Root>
             <Paragraph>
                <String>Unformatierter Text 1</String>
                <String>Unformatierter Text 2</String>
                <Text fontFormat="kursiv">
                   <String>Kursiver Text 1</String>
                   <String>Kursiver Text 2</String>
                </Text>
                <String>Unformatierter Text</String>
                <Text fontFormat="unterstrichen">
                   <String>unterstrichener Text 1</String>
                   <String>unterstrichener Text 2</String>
                   <String>unterstrichener Text 3</String>
                </Text>
             </Paragraph>
             <Paragraph>
                <String>Unformatierter Text</String>
             </Paragraph>
          </Root>

          Comment


          • #6
            Hey,

            ich konnte Martins Ansatz inzwischen recht gut umsetzen. Inzwischen hat sich aber auch die Ausgangs-XML-Datei etwas geändert. Es sind weitere Elemente wie z.B. <Warning> und <Error> hinzugekommen:

            Code:
            <Root>
            	<Paragraph>
            		<Line>
            			<String>Unformatierter Text 1</String>
            			<String>Unformatierter Text 2</String>
            			<Font>
            				<FontStyle>kursiv</FontStyle>
            			</Font>
            			<String>Kursiver Text 1</String>
            			<Warning>
            				<Id>1</Id>
            			</Warning>	
            			<String>Kursiver Text 2</String>	
            			<Font>
            				<FontStyle></FontStyle>
            			</Font>
            			<String>Unformatierter Text</String>
            			<Font>
            				<FontStyle>unterstrichen</FontStyle>
            			</Font>
            			<Error>
            				<Id>1</Id>
            			</Error>
            			<String>unterstrichener Text 1</String>	
            			<Error>
            				<Id>2</Id>
            			</Error>
            			<String>unterstrichener Text 2</String>	
            		</Line>	
            		<Line>
            			<String>unterstrichener Text 3</String>
            		<Line>
            	</Paragraph>
            	<Paragraph>
            		<String>Unformatierter Text</String>
            	</Paragraph>
            </Root>
            Verwende ich nun die bisherige Lösung, so wird der Font bzw. FontStyle auch auf die <Warning> und <Error> Elemente angewendet. Ich hätte aber gerne dass der Font bzw. FontStyle nur auf die <String> Elemente angewendet wird.
            Es sollte also so etwas heraus kommen:

            Code:
            <Root>
               <Paragraph>
                  <String>Unformatierter Text 1</String>
                  <String>Unformatierter Text 2</String>
                  <Text fontFormat="kursiv">
                     <String>Kursiver Text 1</String>
                  </Text>
                  <Warning>
                  	<Id>1</Id>
                  </Warning>      
                  <Text fontFormat="kursiv">
                     <String>Kursiver Text 2</String>
                  </Text>
                  <String>Unformatierter Text</String>
                  <Error>
                     <Id>1</Id>
                  </Error>
                  <Text fontFormat="unterstrichen">
                     <String>unterstrichener Text 1</String>
                  </Text>   
                  <Error>
                     <Id>2</Id>
                  </Error>
                  <Text fontFormat="unterstrichen">   
                     <String>unterstrichener Text 2</String>
                     <String>unterstrichener Text 3</String>
                  </Text>
               </Paragraph>
               <Paragraph>
                  <String>Unformatierter Text</String>
               </Paragraph>
            </Root>
            Könntet ihr mir vielleicht weiterhelfen? Ich bin schon fast am verzweifeln

            Comment


            • #7
              Wenn man ein weiteres for-each-group, diesmal aber mit group-adjacent einbaut, sollte man das lösen können:
              Code:
              <xsl:stylesheet
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="2.0">
                
                <xsl:strip-space elements="*"/>
                <xsl:output indent="yes"/>
                
                <xsl:template match="@* | node()">
                  <xsl:copy>
                    <xsl:apply-templates select="@* , node()"/>
                  </xsl:copy>
                </xsl:template>
                
                <xsl:template match="Root">
                  <xsl:copy>
                    <xsl:apply-templates/>
                  </xsl:copy>
                </xsl:template>
                
                <xsl:template match="Paragraph">
                  <xsl:copy>
                    <xsl:for-each-group select="Line/* | *[not(self::Line)]" group-starting-with="Font">
                      <xsl:choose>
                        <xsl:when test="self::Font[normalize-space(FontStyle)]">
                          <xsl:variable name="fStyle" select="FontStyle"/>
                          <xsl:for-each-group select="current-group() except ." group-adjacent="boolean(self::String)">
                            <xsl:choose>
                              <xsl:when test="current-grouping-key()">
                                <Text fontFormat="{$fStyle}">
                                  <xsl:copy-of select="current-group()"/>
                                </Text>
                              </xsl:when>
                              <xsl:otherwise>
                                <xsl:apply-templates select="current-group()"/>
                              </xsl:otherwise>
                            </xsl:choose>
                          </xsl:for-each-group>
                        </xsl:when>
                        <xsl:otherwise>
                          <xsl:copy-of select="current-group()[not(self::Font[not(normalize-space(FontStyle))])]"/>
                        </xsl:otherwise>
                      </xsl:choose>
                    </xsl:for-each-group>
                  </xsl:copy>
                </xsl:template>
                
              </xsl:stylesheet>
              Damit macht Saxon 9 aus
              Code:
              <Root>
              	<Paragraph>
              		<Line>
              			<String>Unformatierter Text 1</String>
              			<String>Unformatierter Text 2</String>
              			<Font>
              				<FontStyle>kursiv</FontStyle>
              			</Font>
              			<String>Kursiver Text 1</String>
              			<Warning>
              				<Id>1</Id>
              			</Warning>	
              			<String>Kursiver Text 2</String>	
              			<Font>
              				<FontStyle></FontStyle>
              			</Font>
              			<String>Unformatierter Text</String>
              			<Font>
              				<FontStyle>unterstrichen</FontStyle>
              			</Font>
              			<Error>
              				<Id>1</Id>
              			</Error>
              			<String>unterstrichener Text 1</String>	
              			<Error>
              				<Id>2</Id>
              			</Error>
              			<String>unterstrichener Text 2</String>	
              		</Line>	
              		<Line>
              			<String>unterstrichener Text 3</String>
              		</Line>
              	</Paragraph>
              	<Paragraph>
              		<String>Unformatierter Text</String>
              	</Paragraph>
              </Root>
              dann
              Code:
              <Root>
                 <Paragraph>
                    <String>Unformatierter Text 1</String>
                    <String>Unformatierter Text 2</String>
                    <Text fontFormat="kursiv">
                       <String>Kursiver Text 1</String>
                    </Text>
                    <Warning>
                       <Id>1</Id>
                    </Warning>
                    <Text fontFormat="kursiv">
                       <String>Kursiver Text 2</String>
                    </Text>
                    <String>Unformatierter Text</String>
                    <Error>
                       <Id>1</Id>
                    </Error>
                    <Text fontFormat="unterstrichen">
                       <String>unterstrichener Text 1</String>
                    </Text>
                    <Error>
                       <Id>2</Id>
                    </Error>
                    <Text fontFormat="unterstrichen">
                       <String>unterstrichener Text 2</String>
                       <String>unterstrichener Text 3</String>
                    </Text>
                 </Paragraph>
                 <Paragraph>
                    <String>Unformatierter Text</String>
                 </Paragraph>
              </Root>

              Comment

              Working...
              X