Produire des données de contexte pour la première et la dernière occurrence de chaque valeur d'un élément

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

  •  03-07-2019
  •  | 
  •  

Question

Étant donné le code XML suivant:

<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>

je voudrais retourner quelque chose comme

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

À l'aide d'un ensemble de nœuds, j'ai pu obtenir la dernière occurrence via:

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

mais je n'arrive pas à comprendre comment obtenir le premier.

Était-ce utile?

La solution

Pour obtenir les première et dernière occurrences (ordre des documents) dans chaque " <val> " groupe, vous pouvez utiliser un <xsl:key> comme ceci:

<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: Une petite explication car cela pourrait ne pas être évident tout de suite:

  • Le <container> retourne tous les key() nœuds ayant un <xsl:variable> donné. Vous utilisez la fonction //container pour l'interroger.
  • Le generate-id() est l'endroit où tout se passe. Il se lit comme suit:
    • pour chacun des key('ContainerGroupByVal', val) nœuds du document (" <xsl:for-each> ") cochez & # 8230;
    • & # 8230; s'il a le même identifiant unique (<xsl:apply-templates>) que le premier nœud renvoyé par // ou le dernier nœud renvoyé par /containers/container
    • /*/container renvoie l'ensemble des | nœuds correspondant au
    • actuel
    • si les identifiants uniques correspondent, incluez le nœud dans la sélection
  • le . | . | . fait la sortie. Cela pourrait tout aussi bien être un key(…)[1].

EDIT # 2: Comme Dimitre Novatchev l’indique à juste titre dans les commentaires, vous devez vous garder de l’utilisation de la " key(…)[last()] " XPath sténographie. Si vous pouvez l'éviter, bien sûr, faites-le & # 8212; en partie parce qu'il peut potentiellement sélectionner des nœuds que vous ne voulez pas et surtout parce qu'il est plus lent qu'une expression XPath plus spécifique. Par exemple, si votre document ressemble à:

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

alors vous devriez utiliser " <=> " ou " <=> " au lieu de " <=> ".

EDIT # 3: Une autre syntaxe de ce qui précède serait:

<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>

Explication: L'opérateur d'union XPath " <=> " combine ses arguments dans un ensemble de nœuds. Par définition, un ensemble de nœuds ne peut pas contenir de nœuds en double & # 8212; par exemple: " <=> " créera un ensemble de nœuds contenant exactement un nœud (le nœud actuel).

Cela signifie que si nous créons un ensemble de nœuds d'union à partir du nœud actuel (& ";. &";), le & "; <=> &"; noeud et le " <=> " nœud, son nombre de nœuds sera 2 si (et seulement si) le nœud actuel est égal à l'un des deux autres nœuds, dans tous les autres cas, le nombre sera 3.

Autres conseils

XPath de base:

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

Voici un ensemble de fonctions XPath plus en détail.

I. XSLT 1.0

Fondamentalement la même solution que celle de Tomalak, mais plus compréhensible En outre, elle est complète. Il vous suffit donc de copier et coller le document XML et la transformation, puis d'appuyer sur le bouton " Transformer " bouton de votre IDE XSLT préféré :

<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>

lorsque cette transformation est appliquée sur le document XML fourni à l'origine (modifié pour être bien formé):

<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>

le résultat souhaité est généré :

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

Notez :

  1. Nous utilisons la méthode muenchienne pour regrouper afin de trouver un container élément pour chaque ensemble d'éléments de ce type ayant la même valeur pour val.
  2. De la liste de nœuds entière de <xsl:for-each-group> éléments ayant la même current-group() valeur , nous produisons les données requises pour le premier <=> élément du groupe et pour le dernier <= > élément du groupe.

II. XSLT 2.0

Cette 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>

appliqué sur le même document XML que ci-dessus, génère le résultat souhaité :

<*>

Notez :

  1. Utilisation de l'instruction <=> XSLT.

  2. Utilisation de la <=> fonction.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top