Produzieren Kontextdaten zum ersten und letzten Auftreten eines jeden Wertes eines Elements

StackOverflow https://stackoverflow.com/questions/807578

  •  03-07-2019
  •  | 
  •  

Frage

In Anbetracht der folgenden xml:

<container>
    <val>2</val>
    <id>1</id>
</container>
<container>
    <val>2</val>
    <id>2</id>
</container>
<container>
    <val>2</val>
    <id>3</id>
</container>
<container>
    <val>4</val>
    <id>1</id>
</container>
<container>
    <val>4</val>
    <id>2</id>
</container>
<container>
    <val>4</val>
    <id>3</id>
</container>

Ich möchte etwas zurückgeben wie

2 - 1
2 - 3
4 - 1
4 - 3

Mit einem nodeset konnte ich über das letzte Vorkommen erhalten:

exsl:node-set($list)/container[not(val = following::val)]

, aber ich kann nicht herausfinden, wie die ersten zu erhalten.

War es hilfreich?

Lösung

Um das erste und das letzte Vorkommen (Dokumentreihenfolge) in jeder „<val>“ Gruppe zu erhalten, können Sie eine <xsl:key> wie folgt verwenden:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="text" />

  <xsl:key name="ContainerGroupByVal" match="container" use="val" />

  <xsl:variable name="ContainerGroupFirstLast" select="//container[
    generate-id() = generate-id(key('ContainerGroupByVal', val)[1])
    or
    generate-id() = generate-id(key('ContainerGroupByVal', val)[last()])
  ]" />

  <xsl:template match="/">
    <xsl:for-each select="$ContainerGroupFirstLast">
      <xsl:value-of select="val" />
      <xsl:text> - </xsl:text>
      <xsl:value-of select="id" />
      <xsl:value-of select="'&#10;'" /><!-- LF -->
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

EDIT # 1: Ein bisschen eine Erklärung, da dies möglicherweise nicht offensichtlich sofort:

  • Die <xsl:key> gibt alle <container> Knoten einen bestimmten <val> haben. Sie verwenden die Funktion key() es abzufragen.
  • Die <xsl:variable> ist, wo alles passiert. Es liest sich wie:
    • für jede der <container> Knoten im Dokument ( "//container") überprüfen ...
    • ... wenn es die gleiche eindeutige ID (generate-id()) als erster Knoten durch key() oder den letzten Knoten von key() zurückgegeben zurückgegeben
    • wo key('ContainerGroupByVal', val) den Satz von <container> Knoten gibt den aktuellen <val> passende
    • , wenn die eindeutigen IDs übereinstimmen, umfassen den Knoten in der Auswahl
  • die <xsl:for-each> tut die Ausgabe. Es könnte genauso gut ein <xsl:apply-templates> sein.

EDIT # 2: Wie Dimitre Novatchev zu Recht in den Kommentaren darauf hin, sollten Sie vorsichtig sein, um die „//“ XPath Stenographie zu verwenden. Wenn Sie es, mit allen Mitteln vermeiden können, tun dies - zum Teil, weil es möglicherweise Knoten wählt die Sie nicht wollen, und vor allem, weil es langsamer ist als eine spezifischere XPath-Ausdruck. Zum Beispiel, wenn Sie Ihr Dokument wie folgt aussieht:

<containers>
  <container><!-- ... --></container>
  <container><!-- ... --></container>
  <container><!-- ... --></container>
</containers>

, dann sollten Sie "/containers/container" oder "/*/container" anstelle von "//container" verwenden.


EDIT # 3: Eine alternative Syntax der oben wäre:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="text" />

  <xsl:key name="ContainerGroupByVal" match="container" use="val" />

  <xsl:variable name="ContainerGroupFirstLast" select="//container[
    count(
        .
      | key('ContainerGroupByVal', val)[1]
      | key('ContainerGroupByVal', val)[last()]
    ) = 2
  ]" />

  <xsl:template match="/">
    <xsl:for-each select="$ContainerGroupFirstLast">
      <xsl:value-of select="val" />
      <xsl:text> - </xsl:text>
      <xsl:value-of select="id" />
      <xsl:value-of select="'&#10;'" /><!-- LF -->
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Erläuterung: Die XPath-Operator union „|“ verbindet es Argumente in eine Knotenmenge ist. Per Definition kann ein Knoten-Set enthält keine doppelten Knoten - zum Beispiel: „. | . | .“ wird ein Knoten-Set erstellen enthält genau einen Knoten (der aktuelle Knoten)

.

Das heißt, wenn wir eine Union Knotenmenge aus dem aktuellen Knoten ( „“), der ‚key(…)[1]‘ Knoten und die ‚key(…)[last()]‘ Knoten erstellen, es Knotenzahl sind wird 2, wenn (und nur dann) die aktuelle Knoten ist gleich einer der beiden anderen Knoten, in allen anderen Fällen die Zählung 3 sein.

Andere Tipps

XPath :

//container[position() = 1]  <- this is the first one
//container[position() = last()]  <- this is the last one

Hier ist eine Reihe von XPath-Funktionen im Detail.

ich. XSLT 1.0

Im Grunde der gleiche Lösung wie die von Tomalak, aber verständlichen Auch ist es vollständig, so dass Sie nur das XML-Dokument und die Transformation kopieren und einfügen müssen und dann drücken Sie einfach die "Transform" Button Ihres Lieblings XSLT IDE :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:key name="kContByVal" match="container"
     use="val"/>

    <xsl:template match="/*">
      <xsl:for-each select=
       "container[generate-id()
                 =
                  generate-id(key('kContByVal',val)[1])
                 ]
       ">

       <xsl:variable name="vthisvalGroup"
        select="key('kContByVal', val)"/>

       <xsl:value-of select=
        "concat($vthisvalGroup[1]/val,
              '-',
              $vthisvalGroup[1]/id,
              '&#xA;'
              )
      "/>
       <xsl:value-of select=
        "concat($vthisvalGroup[last()]/val,
              '-',
              $vthisvalGroup[last()]/id,
              '&#xA;'
              )
        "/>
      </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

, wenn diese Transformation auf dem ursprünglich bereitgestellten XML-Dokument (bearbeitet werden wohlgeformt) angewandt wird:

<t>
    <container>
        <val>2</val>
        <id>1</id>
    </container>
    <container>
        <val>2</val>
        <id>2</id>
    </container>
    <container>
        <val>2</val>
        <id>3</id>
    </container>
    <container>
        <val>4</val>
        <id>1</id>
    </container>
    <container>
        <val>4</val>
        <id>2</id>
    </container>
    <container>
        <val>4</val>
        <id>3</id>
    </container>
</t>

das gewünschte Ergebnis erzeugt :

2-1
2-3
4-1
4-3

Hinweis: :

  1. Wir verwenden die Muench-Methode für die Gruppierung für jeden Satz solcher Elemente ein container Element zu finden, die denselben Wert für val haben.
  2. Aus der ganzen Knoten-Liste der container Elemente mit dem gleichen val Wert , wir gebe die erforderlichen Daten für das erste container Element in der Gruppe und für das letzte container Element in der Gruppe.

II. XSLT 2.0

Diese Transformation :

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="text"/>

    <xsl:template match="/*">
      <xsl:for-each-group select="container"
           group-by="val">
        <xsl:for-each select="current-group()[1], current-group()[last()]">
          <xsl:value-of select=
           "concat(val, '-', id, '&#xA;')"/>
        </xsl:for-each>
    </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

, wenn auf dem gleichen XML-Dokument wie oben angewandt, prodices das gewünschte Ergebnis :

2-1
2-3
4-1
4-3

Hinweis: :

  1. Die Verwendung der <xsl:for-each-group> XSLT-Anweisung.

  2. Die Verwendung der current-group() Funktion.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top