Announcement

Collapse
No announcement yet.

Rekursive Sortierung

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

  • Rekursive Sortierung

    Moin,

    ich habe zwei große XML-Dateien, die in etwa wie folgt aussehen:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <Report Type="AllRights"
      Description="Komplette Übersicht der konfigurierten Zugriffsrechte eines Mandanten"
      Date="2016-07-04T15:42:23.290+02:00">
      <Server
        CSBServerURL="https://domain.tld:8443/sedna-transfer-service-xf/services"/>
      <SecurityClass Name="ActiveXWindowDefinitions" Alias="APP - ActiveX Fenster"/>
      <SecurityClass Name="FolderDisplays" Alias="REC - Aktenanzeigen">
        <Object ID="322d2ace-b741-4daa-bbb0-9ca93a46f743" Name="Aktenanzeige" Alias="Aktenanzeige"
          OwnerName="Supervisor" OwnerType="Person" OwnerID="00000004-0005-9000-0000-000000000003"
          NamespaceGUID="00000001-0001-9000-0000-000000000002" NamespaceWithName="dom:Aktenanzeige"
          FullyQualifiedName="http://com.domain/Aktenanzeige">
          <ObjectRightProperty ID="1073741824" Name="DenySAChangeOwner" Alias="Eigentümer ändern"
            Mask="0"/>
          <ObjectRightProperty ID="2147483648" Name="DenySADelegate" Alias="Weitergeben" Mask="0"/>
          <AccessIdentifier ID="00000004-0007-9000-0000-000000000002" Type="Role" Name="admins"
            Alias="admins">
            <Right ID="2" Name="delete" Alias="Administrativ - Löschen" Allow="1" Deny="0" Delegate="0"/>
            <Right ID="1" Name="update" Alias="Administrativ - Editieren" Allow="1" Deny="0"
              Delegate="0"/>
          </AccessIdentifier>
        </Object>
        <Object ID="17e42e1b-b91f-4d35-b0e4-2cc5f3e43eaf" Name="Ablage" Alias="Ablage"
          OwnerName="Supervisor" OwnerType="Person" OwnerID="00000004-0005-9000-0000-000000000003"
          NamespaceGUID="00000001-0001-9000-0000-000000000002" NamespaceWithName="dom:Ablage"
          FullyQualifiedName="http://com.org/Ablage">
          <ObjectRightProperty ID="1073741824" Name="DenySAChangeOwner" Alias="Eigentümer ändern"
            Mask="0"/>
          <ObjectRightProperty ID="2147483648" Name="DenySADelegate" Alias="Weitergeben" Mask="0"/>
          <AccessIdentifier ID="00000004-0007-9000-0000-000000000002" Type="Role" Name="admins"
            Alias="admins">
            <Right ID="2" Name="delete" Alias="Administrativ - Löschen" Allow="1" Deny="0" Delegate="0"/>
            <Right ID="1" Name="update" Alias="Administrativ - Editieren" Allow="1" Deny="0"
              Delegate="0"/>
          </AccessIdentifier>
        </Object>
      </SecurityClass>
      <!-- hier kommen viele weitere SecurityClass elemente -->
    </Report>
    Um zwei solcher Reports vernünftig abgleichen zu können, möchte ich nun die Reports vollständig rekursiv sortieren.
    Ich habe bereits etwas mit <xsl:sort> experimentiert, aber die Ergebnisse waren eher bescheiden.
    Ideal wäre es, wenn die SecurityClass-Elemente nach ID und Name sortiert würden, die Ausbauvariante auch noch alle Attribute zu sortieren, spare ich mir für später ...

    Meine Vorstellung wäre
    - Kopiere das Root-Element
    - apply-templates für alle Elemente
    - template für das Server-Element, apply-templates für alle SecurityClass-Elemente sortiert nach ID und Name
    - template für das SecurityClass-Element, apply-templates für alle Child-Elemente sortiert nach ID und Name
    - template für alle Elemente, copy-of des Elements, falls Child-Elemente existieren apply-templates für alle Child-Elemente

    Ist der Ansatz richtig?
    Kann mir jemand ein grobes Raster vorgeben?

    Vielen Dank im voraus!
    --
    Cheers Vince

  • #2
    Das ist der richtige Ansatz, solange du für ein Element nichts ändern willst (wie das Report-Element und andere), reicht ein generisches
    Code:
    <xsl:template match="@* | node()">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
    </xsl:template>
    nur für die Elemente wie Server und SecurityClass kannst du dann spezielle Template(s) mit der Sortierung schreiben.

    Das Sortieren von Attributen könnte aber scheitern, da die im Datenmodell von XSLT und in XML im allgemeinen keine Ordnung haben, da musst du ausprobieren, ob dein XSLT-Prozessor eine Sortierung dann auch in der Reihenfolge bei der Ausgabe des Resultates berücksichtigt.

    Comment


    • #3
      Moin,

      ich habe eine fast funktionierende Version:
      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">
        
        <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
        
        <xsl:template match="/">
          <xsl:apply-templates select="*"/>
        </xsl:template>
        
        <xsl:template match="*">
          <xsl:choose>
            <xsl:when test="count(child::*) > 0">
              <xsl:element name="{name()}">
                <!-- xsl:apply-templates for all attributes and xsl:attribute for each? -->
                <xsl:apply-templates select="child::*">
                  <xsl:sort select="@ID" data-type="number"/>
                  <xsl:sort select="@Name"/>
                </xsl:apply-templates>
              </xsl:element>
            </xsl:when>
            <xsl:otherwise>
              <xsl:copy-of select="."/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:template>
        
      </xsl:stylesheet>
      Jetzt muß ich nur noch ein Extra-Template machen, mit dem ich alle Attribute eines Elements parse und sie mit <xsl:attribute> dem Element hinzufüge. <xsl:copy-of> funktioniert leider an dieser Stelle nicht, da auch alle Kind-Elemente mitkopiert würden.

      Ich bin bei meinen Recherchen immer wieder auf den "node()|@*"-Match gestoßen, aber ich verstehe nicht, warum ich den brauchen sollte - es geht doch auch so?

      Ansonsten bin ich wie immer für weitere Anregungen dankbar ...
      --
      Cheers Vince

      Comment


      • #4
        Es gibt ja schon die eingebauten Templates https://www.w3.org/TR/xslt#built-in-rule mit u.a.
        Code:
        <xsl:template match="*|/">
          <xsl:apply-templates/>
        </xsl:template>
        da braucht es dein
        Code:
          <xsl:template match="/">
            <xsl:apply-templates select="*"/>
          </xsl:template>
        nicht.

        Ansonsten reicht statt
        Code:
        <xsl:element name="{name()}">
        auch ein
        Code:
        <xsl:copy>
        , hat aber den Vorteil, dass der Namensraum mit kopiert wird, und auch andere eventuell vorhandene Namensräume.

        Und ein Template, das dann per xsl: choose eine Bedingung überprüft, um verschiedene Inhalte zu konstruieren, kann auch durch zwei Templates ersetzt werden:
        Code:
          <xsl:template match="*[*]">
                <xsl:copy>
                  <!-- xsl:apply-templates for all attributes and xsl:attribute for each? -->
                  <xsl:apply-templates select="@*"/
                  <xsl:apply-templates select="*">
                    <xsl:sort select="@ID" data-type="number"/>
                    <xsl:sort select="@Name"/>
                  </xsl:apply-templates>
                </xsl:copy>
          </xsl:template>
        nur wenn du das bereits vorgeschlagene
        Code:
        <xsl:template match="@* | node()">
          <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
          </xsl:copy>
        </xsl:template>
        als Basis benutzt, brauchst du das zweite Template (mit dem xsl: copy-of) gar nicht und die Attribute werden damit auch kopiert.

        Es reicht dann also

        Code:
        <xsl:template match="@* | node()">
          <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
          </xsl:copy>
        </xsl:template>
        
          <xsl:template match="*[*]">
                <xsl:copy>
                  <xsl:apply-templates select="@*"/
                  <xsl:apply-templates select="*">
                    <xsl:sort select="@ID" data-type="number"/>
                    <xsl:sort select="@Name"/>
                  </xsl:apply-templates>
                </xsl:copy>
          </xsl:template>
        oder bei der Ausgangsbeschreibung auch

        Code:
        <xsl:template match="@* | node()">
          <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
          </xsl:copy>
        </xsl:template>
        
          <xsl:template match="Server | SecurityClass">
                <xsl:copy>
                  <xsl:apply-templates select="@*"/
                  <xsl:apply-templates select="*">
                    <xsl:sort select="@ID" data-type="number"/>
                    <xsl:sort select="@Name"/>
                  </xsl:apply-templates>
                </xsl:copy>
          </xsl:template>

        Comment


        • #5
          Moin,

          das ist echt interessant - auf diesen Ansatz wäre ich selber nie gekommen- vielen Dank!
          Noch eine Frage zum Verständnis: Was genau matcht "*[*]"? Für mich liest sich das wie "jede Node, egal welche Bedingung ([]) gegeben ist" - aber das wäre ja "*", oder nicht?

          Und jetzt habe ich noch ein lustiges Detailproblem: Das Attribut ID enthält meist Zahlenwerte, ab und zu aber auch alphanumerische Identifier. Gibt es eine Variante für xsl:sort, bei der ich primär numerisch und subsidiär alphanumerisch auswerten kann? Ich habe es mit zwei xsl:sort hintereinander probiert, aber vermutlich wird er schon beim ersten Auftreten von ID fündig und versucht es numerisch zu deuten, was bei den alphanumerischen Werten fehlschlägt.
          --
          Cheers Vince

          Comment


          • #6
            match="*[*]" ist ein Pattern für Elemente, die mindestens ein Kindelement haben, also eine andere (und gängigere) Formulierung von match="*[count(*) > 1]".

            Was das Sortieren angeht, der Ansatz mit zwei xsl:sort scheint mir richtig, aber wenn es das selbe Attribut ist, ist mir noch nicht klar, welche Sortierung herauskommen soll. Vielleicht kannst du mal anhand eines Beispieldatensatz erklären, welche Sortieren du erreichen möchtest.

            Und es ist auch wichtig, zu wissen, ob du XSLT 2.0 oder nur 1.0 verwendest, da mit XSLT 2.0 und damit XPath 2.0 das Überprüfen mit ausdrücken wie @id castable as xs:integer einfach und kompakt möglich ist.

            Comment


            • #7
              Moin,

              das Thema mit der Sortierung hat sich quasi in Wohlgefallen aufgelöst, da Elemente mit alphanumerischen IDs wie 77829224-4740-4d3e-83d6-c16e03476c0b doch lexikographisch korrekt zu sortiert werden scheinen und zudem keine Mischformen auftreten.

              Ich verwende im übrigen (leider) fast immer XSLT 1.0, da die meisten Browser kein XSLT 2.0 beherrschen. :'(
              --
              Cheers Vince

              Comment

              Working...
              X