2
votes

Aplatissement avec XSLT: je veux déplacer un élément kind d'un niveau

J'ai un fichier XML où les éléments B sont à l'intérieur des éléments A et je souhaite les déplacer vers le haut. De:

<A><C>No</C>
   <D>May be</D></A>

Je voudrais avoir:

<A><C>No</C></A>
<A><D>May be</D></A>

Le programme XSLT le plus proche que j'ai est celui-ci:

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs" version="1.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="A">
      <xsl:for-each select="*">
    <xsl:choose>
      <xsl:when test="name()='B'">
            <xsl:apply-templates select="."/>
      </xsl:when>
      <xsl:otherwise>
                  <xsl:element name="A">
                      <xsl:apply-templates select="."/>
          </xsl:element>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

Il a deux problèmes: il ignore les nœuds de texte (il s'agit probablement simplement d'ajouter | text () au select = "*") mais, plus important, il crée un élément pour chaque nœud alors que je voudrais qu'ils restent ensemble sous un seul. Par exemple, la feuille de style ci-dessus fait:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <A>
    <C>Text</C>
    Text again

    More text
    <D>Other text</D>
  </A>
  <B>Text again</B>
  <A>
    <C>No</C>
    <D>May be</D>
  </A>
  <B>What?</B>
  <A>
    Something
  </A>
  <B>Nothing</B>
  <A>
    <D>Again</D>
  </A>
  <B>Content</B>
  <A>
    End
  </A>
</root>

où je veux:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <A>
    <C>Text</C>
    Text again

    More text
    <D>Other text</D>
    <B>Text again</B>
    <C>No</C>
    <D>May be</D>
    <B>What?</B>
  </A>
  <A>
    Something
    <B>Nothing</B>
    <D>Again</D>
    <B>Content</B>
    End
  </A>
</root>

Dans mes fichiers XML, sont toujours directs enfants de, et il n'y a pas ou imbrication.

Le cas d'utilisation principal est la production de HTML où UL et OL ne peuvent pas être dans un P.

Cette question est liée mais pas identique à xslt aplatissement des éléments enfants dans un élément DocBook para a > (et peut également être Aplatir la hiérarchie XML à l'aide de XSLT )


2 commentaires

Votre sortie montre plus (bien plus) que le simple déplacement des éléments B jusqu'à l'élément root . Vous divisez également les éléments A à ce stade, ce qui va être plus (beaucoup plus) difficile (au moins dans XSLT 1.0). Quelle est l'importance de cette partie?


@ michael.hor257k Oui, il est très important que B se déplace vers le haut et dans le processus, divise son ancien parent A en deux.


3 Réponses :


1
votes

Une solution XSLT-1.0 - qui est assez moche - est la suivante. La sortie est comme vous le souhaitez, mais uniquement pour ce simple MCVE. Une solution générale serait beaucoup plus compliquée que @ michael.hor257k mentionné dans les commentaires. Sans plus de données, il est peu probable de créer une meilleure solution dans XSLT-1.0. Les solutions pour XSLT-2.0 et supérieur peuvent simplifier cela.

<A>
    <C>No</C>
    <D>May be</D>
</A>

Concernant la situation de

<A><C>No</C></A>
<A><D>May be</D></A>

Elle est gérée de manière appropriée dans le au-dessus du code. Son résultat est donc

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

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:for-each select="A">
                <xsl:if test="normalize-space(text()[1])">
                    <A>
                        <xsl:copy-of select="text()[1]" />
                    </A>
                </xsl:if>
                <xsl:if test="preceding::*">
                    <xsl:copy-of select="B[1]" />
                </xsl:if>
                <A>
                    <xsl:copy-of select="C[1] | C[1]/following-sibling::text()[1] | D[1]" />
                </A>
                <xsl:if test="not(preceding::*)">
                    <xsl:copy-of select="B[1]" />
                </xsl:if>
                <A>
                    <xsl:copy-of select="C[2] | C[2]/following-sibling::text()[1]" />
                    <xsl:if test="D[2]">
                        <xsl:copy-of select="D[2]" />
                    </xsl:if>
                </A>
                <xsl:copy-of select="B[2]" />
                <xsl:if test="normalize-space(text()[last()])">
                    <A>
                        <xsl:copy-of select="text()[last()]" />
                    </A>
                </xsl:if>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>         

</xsl:stylesheet>


0 commentaires

0
votes

Facile en XSLT 2 ou 3 avec group-adjacent = ". instance of element (B)" ou group-adjacent = "boolean (self :: B)" code >, voici un exemple XSLT 3 (XSLT 3 est pris en charge par Saxon 9.8 ou 9.9 sur Java et .NET ( https://sourceforge.net/projects/saxon/files/Saxon-HE/ ) et par Altova depuis les versions 2017):

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="A">
      <xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
          <xsl:choose>
              <xsl:when test="current-grouping-key()">
                  <xsl:apply-templates select="current-group()"/>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:element name="{name(..)}" namespace="{namespace-uri(..)}">
                      <xsl:apply-templates select="current-group()"/>
                  </xsl:element>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>
  </xsl:template>

</xsl:transform>

https://xsltfiddle.liberty-development.net/gWmuiKv

Dans XSLT 2 vous devez épeler le comme modèle de transformation d'identité et utiliser xsl: element à la place xsl: copy :

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="A">
      <xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
          <xsl:choose>
              <xsl:when test="current-grouping-key()">
                  <xsl:apply-templates select="current-group()"/>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:copy select="..">
                      <xsl:apply-templates select="current-group()"/>
                  </xsl:copy>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>
  </xsl:template>

</xsl:stylesheet>

http: //xsltransform.hikmatu.com/pPqsHT2


2 commentaires

Pas de processeur XSLT v3 pour ma machine Debian, semble-t-il (le plus récent est XSLT 2, avec Saxon B)


Saxon 9.8 ou 9.9 HE sont disponibles sur Sourceforge pour permettre à toute personne disposant d'un JRE Java récent (je pense 1.6 pour 9.8 et 1.8 pour 9.9) de l'exécuter. Je ne sais pas pourquoi Saxon B est "disponible" pour Debian mais pas Saxon 9.8 ou 9.9. J'ai ajouté une transcription XSLT 2 de la solution XSLT 3 précédente.



2
votes

Comme je l'ai dit dans le commentaire de votre question, il ne s'agit pas de déplacer les éléments vers le haut dans la hiérarchie. Il s'agit de regrouper les nœuds et de créer un nouvel élément A parent pour chaque groupe déterminé par l'élément de division B .

Dans XSLT 1.0 ceci peut être réalisé en utilisant une récursion fraternelle :

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/root">
    <xsl:copy>
        <xsl:apply-templates select="A"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="A">
    <xsl:copy>
        <xsl:apply-templates select="node()[1][not(self::B)]" mode="sibling"/>
    </xsl:copy>
    <xsl:apply-templates select="B[1]" mode="sibling"/>
</xsl:template>

<xsl:template match="node()" mode="sibling">
    <xsl:copy-of select="." />
    <xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
</xsl:template>

<xsl:template match="B" mode="sibling">
    <xsl:copy-of select="." />
    <xsl:if test="following-sibling::node()[normalize-space()]">
        <A>
            <xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
        </A>
        <xsl:apply-templates select="following-sibling::B[1]" mode="sibling"/>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

p >


0 commentaires