Frage

Als relativ neu in XSLT Ich habe, was ich hoffe, eine einfache Frage. Ich habe ein paar flachen XML-Dateien, die ziemlich groß sein können (z. B. 7 MB), die ich brauche ‚hierarchischen‘ zu machen. Zum Beispiel könnte die flache XML wie folgt aussehen:

<D0011>
    <b/>
    <c/>
    <d/>
    <e/>
    <b/>
    ....
    ....
</D0011>

und es sollte am Ende wie folgt suchen:

<D0011>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
  <b>
 ....
 ....
</D0011>

Ich habe eine funktionierende XSLT für das, und es wird im wesentlichen einen nodeset aller b-Elemente und verwendet dann das ‚following-sibling‘ Achse eine nodeset der Knoten erhalten nach den aktuellen b Knoten (dh. Folgende Geschwister :: * [position () = $ nodePos]). Dann wird Rekursion verwendet, um die Geschwister in den Ergebnisbaum hinzuzufügen, bis ein anderer b Element gefunden wird (ich habe es natürlich parametrisiert, um es noch generisch).

Ich habe auch eine Lösung, die nur die Position Knoten und wählt die Knoten nach dem einem in der XML der nächsten b nach der anderen (unter Verwendung von Rekursion) sendet über eine * [position () = $ nodePos] Auswahl.

Das Problem ist, dass die Zeit, um die Transformation erhöht unakzeptabel mit der Größe der XML-Datei auszuführen. Ein Blick in das mit XML Spy scheint es, dass es die ‚folgende Geschwister‘ und ‚Position () =‘, die die Zeit in den beiden jeweiligen Methoden erfolgen.

Was ich wirklich brauchen, ist eine Möglichkeit, die Anzahl der Knoten in der obigen Auswahl einzuschränken, so dass weniger Vergleiche durchgeführt werden: jedes Mal, wenn die Position geprüft wird, wird jeder Knoten in dem nodeset getestet, um zu sehen, ob seine Position die richtige ist . Gibt es eine Möglichkeit, das zu tun? Alle anderen Vorschläge?

Danke,

Mike

War es hilfreich?

Lösung

Yes there is a way to do it much more efficiently: See Muenchian grouping. If having looked at this you need more help with the details, let us know. The key you'll need is something like:

<xsl:key name="elements-by-group" match="*[not(self::b)]"
   use="generate-id(preceding-sibling::b[1])" />

Then you can iterate over the <b> elements, and for each one, use key('elements-by-group', generate-id()) to get the elements that immediately follow that <b>.

The task of "making the XML more hierarchical" is sometimes called up-conversion, and your scenario is a classic case for it. As you may know, XSLT 2.0 has very useful grouping features that are easier to use than the Muenchian method.

In your case it sounds like you would use <xsl:for-each-group group-starting-with="b" /> or, to parameterize the element name, <xsl:for-each-group group-starting-with="*[local-name() = 'b']" />. But maybe you already considered that and can't use XSLT 2.0 in your environment.

Update:

In response to the request for parameterization, here's a way to do it without a key. Note though that it may be much slower, depending on your XSLT processor.

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name() = $sep]">
      <xsl:copy>
         <xsl:copy-of select="following-sibling::*[not(local-name() = $sep)
               and generate-id(preceding-sibling::*[local-name() = $sep][1]) =
                    generate-id(current())]" />
      </xsl:copy>
   </xsl:for-each>      
</xsl:template>

As noted in the comment, you can keep the performance benefit of keys by defining several different keys, one for each possible value of the parameter. You then select which key to use by using an <xsl:choose>.

Update 2:

To make the group-starting element be defined based on /*/*[2], instead of based on a parameter, use

<xsl:key name="elements-by-group"
   match="*[not(local-name(.) = local-name(/*/*[2]))]"
   use="generate-id(preceding-sibling::*
                           [local-name(.) = local-name(/*/*[2])][1])" />

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name(.) = local-name(../*[2])]">
      <xsl:copy>
         <xsl:copy-of select="key('elements-by-group', generate-id())"/>
      </xsl:copy>
   </xsl:for-each>
</xsl:template>

Andere Tipps

<xsl:key name="k1" match="D0011/*[not(self::b)]" use="generate-id(preceding-sibling::b[1])"/>

<xsl:template match="D0011">
  <xsl:copy>
    <xsl:apply-templates select="b"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="D0011/b">
  <xsl:copy>
    <xsl:copy-of select="key('k1', generate-id())"/>
  </xsl:copy>
</xsl:template>

This is the fine grained trasversal pattern:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="b[1]" name="group">
        <xsl:copy>
            <xsl:apply-templates select="following-sibling::node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::b[1]" mode="group"/>
    </xsl:template>
    <xsl:template match="b[position()!=1]"/>
    <xsl:template match="b" mode="group">
        <xsl:call-template name="group"/>
    </xsl:template>
</xsl:stylesheet>

Output:

<D0011>
    <b>
        <c></c>
        <d></d>
        <e></e>
    </b>
    <b>
    ....
    ....
    </b>
</D0011>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top