Announcement

Collapse
No announcement yet.

Knoten kopieren

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

  • Knoten kopieren

    Hallo zusammen!

    Ich habe folgendes Problem:

    Teile einer XML müssen kopiert werden, was im Grunde mit dem Befehl copy-of ginge. Problem: Leere Knoten wie
    Code:
     
    <node>
    </node>
    werden genauso kopiert. Es ist allerdings ein Ergebnis erwünscht, bei dem die leeren Knoten so dargestellt werden würden:
    Code:
     
    <node/>
    das ist unbedingt wichtig.

    Wahrscheinlich gehe ich wieder den schwierigen weg, aber ich mache Folgendes: an der entsprechenden Stelle wird ein template "copy" aufgerufen:
    Code:
    <xsl:template name="copy">
          <xsl:copy copy-namespaces="no">
            <xsl:copy-of select="@*[not(contains(name(.), 'xmlns:xsi'))]" copy-namespaces="no"/>
    			<xsl:choose>
    				<xsl:when test="name(descendant)!='' ">
    					<xsl:call-template name="copy"/>
    				</xsl:when>
    				<xsl:otherwise>
    					<xsl:value-of select="."/>
    				</xsl:otherwise>
    			</xsl:choose>
          </xsl:copy>
       </xsl:template>
    was ich erreichen möchte ist, dass bei vorhandenen Kinderelementen das template so oft aufgerufen wird, bis alle Kinder samt Inhalt kopiert worden sind. Es geht wirklich nur ums kopieren, zum Testen kann eine beliebige XML genommen werden.

    Ich wollte das so lösen, dass ich nach dem Namen des Kindes frage, ob es nicht leer ist. Was ja bedeuten würde, ob es ein Kindelement gibt... Aber irgendwo habe ich anscheinend einen Denkfehler.

    Ich bekomme es nicht hin, dass die Unterknoten richtig kopiert werden. Bei unterschiedlichen Variationen mit dem dargestellten template schaffe ich es alle Knoten ohne Inhalt zu Kopieren, oder Knoten und Inhalt einfach in einer Zeile hintereinander, aber nicht einfach die vorhandene Datei.

    Wie gesagt, das template soll im Grunde die Funktion eines copy-of haben nur werden aus "gestückelten" leeren Elementen hier "einzelne" leere erstellt.


    Vielen Dank für jeden Hinweis im Voraus!!

  • #2
    Vielleicht anders gefragt:

    Wie lautet der Befehl für "ausgeben den Namen des Kindes"?

    Comment


    • #3
      In deinem ersten Beispiel ist der Elementknoten nicht leer, er enthält einen Textknoten, der allerdings nur "white space" Zeichen (also Zeichen wie ) enthält. Aber der Elementknoten ist nicht leer.
      Ansonsten transformiert XSLT einen Eingabebaum in einen Resultatsbaum. Wie dann Knoten aus dem Resultatsbaum serialisiert werden, hängt nicht vom XSLT Code ab, sondern ist Sache des Serialisierers. Du kannst also mit XSLT nicht festlegen, dass ein leeres Element als <node/> oder aber als <node></node> serialisiert wird.

      Comment


      • #4
        Hallo! Danke dafür. Du hast natürlich Recht, was die whitespaces angeht...

        Allerdings habe ich ein Problem mit den Elementen in doppelter Ausführung. Und wenn die so kopiert werden, wie in meinem fehlerhaften template, dann bekommen die zumindest bei Altova die entsprechende Form. Das Problem scheint auch nicht ganz aus der Luft gegriffen zu sein, habe bereits Themen gefunden, wo das diskutiert worden ist...

        Meine Frage also, wie komme ich an den Namen eines Kindes bzw. was wäre eine sinnvolle alternative Abfrage an der entsprechenden Stelle, um festzustellen, ob ein Element Kinder hat?

        Vielen Dank!

        Comment


        • #5
          Code:
          <xsl:if test="*">
            <!-- Kontextknoten hat mindestens ein Kind-Element -->
          </xsl:if>

          Comment


          • #6
            Hallo Martin,

            Vielen Dank! Das ist exakt die richtige Abfrage.

            Woran liegt das eigentlich, dass ich bei meinem template einen "XSLT instruction stack overflow" bekomme? Es sind nur 3 Elemente mit jeweils 5 Kindern, die kopiert werden müssen...
            Code:
            <xsl:template name="copyNode">
                  <xsl:copy copy-namespaces="no">
                    <xsl:copy-of select="@*[not(contains(name(.), 'xmlns:xsi'))]" copy-namespaces="no"/>
            			<xsl:choose>
            				<xsl:when test="*">
            					<xsl:call-template name="copyNode"/>
            				</xsl:when>
            				<xsl:otherwise>
            					<xsl:value-of select="."/>
            				</xsl:otherwise>
            			</xsl:choose>
                  </xsl:copy>
               </xsl:template>
            Ich setze Altova ein...

            Danke!

            Comment


            • #7
              Du rufst dein Template namens "copyNode" rekursiv auf, ohne eine Abbruchbedingung zu haben. Vermutlich willst du beim rekursiven Aufruf den Kontextknoten ändern, aber das ist mit benamten Templates und xsl: call-template nicht möglich, da musst du xsl: apply-templates select="*" verwenden und entsprechend ein match-Attribut für das Template formulieren.

              Comment


              • #8
                Danke schon mal dafür!

                Wie könnte es denn für meinen Fall aussehen? Wie gesagt, ich ändere wirklich nichts, sondern kopiere nur. Es muss aber unbedingt so kopiert werden.

                Habe im Netz das hier gefunden:
                Code:
                <xsl:stylesheet version="1.0"
                      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
                   <xsl:output method="xml"/>
                   <xsl:param name="indent-increment" select="'   '" />
                
                   <xsl:template match="*">
                      <xsl:param name="indent" select="'&#xA;'"/>
                
                      <xsl:value-of select="$indent"/>
                      <xsl:copy>
                        <xsl:copy-of select="@*" />
                        <xsl:apply-templates>
                          <xsl:with-param name="indent"
                               select="concat($indent, $indent-increment)"/>
                        </xsl:apply-templates>
                        <xsl:if test="*">
                          <xsl:value-of select="$indent"/>
                        </xsl:if>
                      </xsl:copy>
                   </xsl:template>
                
                   <xsl:template match="comment()|processing-instruction()">
                      <xsl:copy />
                   </xsl:template>
                
                   <!-- WARNING: this is dangerous. Handle with care -->
                   <xsl:template match="text()[normalize-space(.)='']"/>
                Finde das eigentlich gut, aber ich möchte ja nicht die ganze Datei, sondern nur an einer bestimmten Stelle die Knoten kopieren.

                Ich bekomme es aber nicht so hin, dass die Dateistruktur auch erhalten bleibt... Da gibt es jedenfalls immer sehr lange Zeilen mit dem kopmletten Inhalt und ähnliches... Wie könnte es übertragen auf meinen Fall aussehen? Ich brauche also ein template, dass ich im Haupttemplate, anscheinend per apply-templates aufrufen kann...

                Danke!

                Comment


                • #9
                  Poste mal ein Beispiel-Eingabedokument und dann das zugehörige Resultat, das das Stylesheet erzeugen soll.

                  Comment


                  • #10
                    Start:
                    Code:
                    <package_information>
                    		<designation xsi:nil="true">
                    		</designation>
                    		<designer>aaaaaaaaaaaaaaa</designer>
                    		<date>2001-12-17</date><creation_soft>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</creation_soft>
                    		<delivery_unit>projects</delivery_unit>
                    		<delivery_unit_count>3.14159E0</delivery_unit_count>
                    		<issue xsi:nil="true">
                                    </data_issue>
                    		<language>en-us</language>
                    </package_information>
                    Ziel:
                    Code:
                    <package_information>
                    		<designation xsi:nil="true"/>
                    		<designer>aaaaaaaaaaaaaaa</designer>
                    		<date>2001-12-17</date><creation_soft>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</creation_soft>
                    		<delivery_unit>projects</delivery_unit>
                    		<delivery_unit_count>3.14159E0</delivery_unit_count>
                    		<issue xsi:nil="true"/>
                    		<language>en-us</language>
                    </package_information>

                    Comment


                    • #11
                      Wie gesagt, mit dem geposteten template klappt es auch. Nur habe ich dann teilweise zahlreiche Elemente in einer Zeile stehen....

                      Das war übrigens ein Ausschnitt aus der ganzen XML Datei. Dieser kann irgendwo stehen (wegen <xslaram name="indent-increment" select="' '" />)...

                      Comment


                      • #12
                        Das Beispiel ist noch nicht mal XML, da fehlt die Namensraumdeklaration und das "issue"-Element ist nicht richtig geschlossen.
                        Aber angenommen man hat
                        Code:
                        <package_information
                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                        		<designation xsi:nil="true">
                        		</designation>
                        		<designer>aaaaaaaaaaaaaaa</designer>
                        		<date>2001-12-17</date><creation_soft>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</creation_soft>
                        		<delivery_unit>projects</delivery_unit>
                        		<delivery_unit_count>3.14159E0</delivery_unit_count>
                        		<issue xsi:nil="true">
                                        </issue>
                        		<language>en-us</language>
                        </package_information>
                        dann transformiert dieses Stylesheet
                        Code:
                        <xsl:stylesheet
                            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                            version="2.0">
                            
                            <xsl:output method="xml" indent="yes"/>
                            
                            <xsl:template match="@* | node()">
                              <xsl:copy>
                                <xsl:apply-templates select="@*, node()"/>
                              </xsl:copy>
                            </xsl:template>
                            
                            <xsl:template match="*[@xsi:nil = 'true']/text()[not(normalize-space())]"/>
                            
                        </xsl:stylesheet>
                        mit AltovaXML Tools 2010 die Eingabe in folgende Ausgabe:
                        Code:
                        <package_information xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                                <designation xsi:nil="true"/>
                                <designer>aaaaaaaaaaaaaaa</designer>
                                <date>2001-12-17</date>
                                <creation_soft>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</creation_soft>
                                <delivery_unit>projects</delivery_unit>
                                <delivery_unit_count>3.14159E0</delivery_unit_count>
                                <issue xsi:nil="true"/>
                                <language>en-us</language>
                        </package_information>
                        d.h. alles wird kopiert, nur die Textknoten mit reinem "white space" innerhalb von Elementen mit dem Attribut xsi: nil="true" werden nicht kopiert.

                        Comment


                        • #13
                          Mein Fehler... Wie gesagt, ich habe nur den entsprechenden Ausschnitt reinkopiert. Dieser ist mitten in einer großen XML Datei...

                          Es sieht eher so aus:
                          Code:
                          <node
                            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                          .
                          .
                          .
                              <package_information>
                          		<designation xsi:nil="true">
                          		</designation>
                          		<designer>aaaaaaaaaaaaaaa</designer>
                          		<date>2001-12-17</date><creation_soft>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</creation_soft>
                          		<delivery_unit>projects</delivery_unit>
                          		<delivery_unit_count>3.14159E0</delivery_unit_count>
                          		<issue xsi:nil="true">
                                          </issue>
                          		<language>en-us</language>
                              </package_information>
                          .
                          .
                          .
                          </node>
                          Mit dem rest mache ich eben was anderes...

                          Mein gesamt stylesheet sieht dann z.B. so aus:
                          Code:
                          <xsl:stylesheet
                              xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                              version="2.0">
                              
                              <xsl:output method="xml" indent="yes"/>
                          
                          <xsl:template match="/">
                          		<xsl:for-each select="/*">
                          			<xsl:element name="{name(.)}">
                          				<xsl:for-each select="./*[contains(name(.), 'package_information')]">
                          					<xsl:element name="{name(.)}">
                          						<xsl:apply-templates select="@*, node()"/>
                          						<xsl:element name="package_type">string</xsl:element>
                          					</xsl:element>
                          				</xsl:for-each>
                                                   </xsl:element>
                          		</xsl:for-each>
                          	</xsl:template>
                          
                          
                          <xsl:template match="@* | node()">
                                <xsl:copy>
                                  <xsl:apply-templates select="@*, node()"/>
                                </xsl:copy>
                          </xsl:template>
                              
                          
                          <xsl:template match="*[@xsi:nil = 'true']/text()[not(normalize-space())]"/>
                          
                          
                          </xsl:stylesheet>
                          Es funktioniert alles. Vielen Dank!

                          Das ist nur so eine Schönheitssache, aber das neuerstellte Element "packege_type" und das alte "package_information" werden in einer Zeile geschlossen... Wie gesagt, dein template, wird eben irgendwo aufgerufen...

                          Kann das auch noch behoben werden? Wie gesagt, ist eine reine Schönheitssache für mich... Am sonsten ist dein template noch eleganter, als das eine, was ich gepostet habe!

                          Danke noch einmal dafür!

                          Comment

                          Working...
                          X