2011-06-06 2 views
1

Je veux utiliser XSLT pour modifier:transformation Xslt/comment entité du groupe

<Office Code="1" OtherAttribute="5"> 
    <Customer CustomerId="0010" CodeModifier="A"/> 
    <Customer CustomerId="0011" CodeModifier="B"/> 
    <Customer CustomerId="0012" CodeModifier="B"/> 
</Office> 
<Office Code="2" OtherAttribute="6"> 
    <Customer CustomerId="2010" CodeModifier="A"/> 
    <Customer CustomerId="0011" CodeModifier="C"/> 
</Office> 

dans:

<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010"/> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011"/> 
    <Customer CustomerId="0012"/> 
</Office> 
<Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010"/> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011"/> 
</Office> 

Mes objectifs:

    groupe
  • chaque entité Customer avec le même CodeModifier dans les entités Office. S'il y a plusieurs CodeModifier, j'ajouterai l'entité Office. Le code attribue à Office sera modifié (concaténation de CodeModifier du client dans le bureau)
  • (facultatif mais trivial je pense) supprimer l'attribut CodeModifier et garder tout autre attribut

Est-ce que quelqu'un sait comment faire ?

+0

Bonne question, +1. Voir ma réponse pour une solution XSLT 2.0 complète, très courte et facile. :) –

Répondre

1

Voici une autre approche, en utilisant des modèles correspondant uniquement.


soumis aux épreuves XSLT 1.0 sous 4.0 msxsl

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

    <xsl:key name="kCustomerGroup" match="Customer" 
     use="concat(../@Code, @CodeModifier)" 
     /> 

    <xsl:template match="Office"> 
     <xsl:apply-templates select="Customer[generate-id() 
      = 
      generate-id(key('kCustomerGroup', 
      concat(../@Code, @CodeModifier))[1])]" 
      /> 
    </xsl:template> 

    <xsl:template match="Customer"> 
     <Office 
      Code="{concat(../@Code,@CodeModifier)}" 
      OtherAttribute="{../@OtherAttribute}"> 

      <xsl:apply-templates select="key('kCustomerGroup', 
       concat(../@Code,@CodeModifier))" mode="copy"/> 

     </Office> 
    </xsl:template> 

    <xsl:template match="Customer" mode="copy"> 
     <xsl:copy> 
      <xsl:copy-of select="@CustomerId"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Sorties à:

<?xml version="1.0" encoding="UTF-8"?> 
<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010"/> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011"/> 
    <Customer CustomerId="0012"/> 
</Office> 
<Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010"/> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011"/> 
</Office> 
+0

Vous utilisez les conditions comme je suis. Seulement que votre condition est dans un prédicat et le mien est dans un ''. ;-) Je n'utilise pas de boucle pour faire un travail, si vous regardez de plus près. – Tomalak

+0

Je vois votre point, j'ai édité ma description. –

2

Cette feuille de style, appliqué à votre échantillon d'entrée:

<!-- a key to group Customers by their office code + modifier --> 
<xsl:key name="kCustomerGroup" match="Customer" 
    use="concat(../@Code, @CodeModifier)" 
/> 

<!-- identity template: copies everything that is not handled otherwise --> 
<xsl:template match="node() | @*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node() | @*" /> 
    </xsl:copy> 
</xsl:template> 

<!-- apply templates directly to customers. modify as necessary --> 
<xsl:template match="/"> 
    <xsl:apply-templates select="//Customer" mode="CustomerGroup" /> 
</xsl:template> 

<xsl:template match="Customer" mode="CustomerGroup"> 
    <xsl:variable name="officeCode" select="concat(../@Code, @CodeModifier)" /> 
    <!-- if this Customer is first of his respective group... --> 
    <xsl:if test=" 
    generate-id() 
    = 
    generate-id(key('kCustomerGroup', $officeCode)[1]) 
    "> 
    <!-- use for-each to switch the context node to the parent --> 
    <xsl:for-each select=".."> 
     <xsl:copy> 
     <xsl:apply-templates select="@*" /> 
     <!-- overwrite the @Code attribute --> 
     <xsl:attribute name="Code"> 
      <xsl:value-of select="$officeCode" /> 
     </xsl:attribute> 
     <!-- now handle the Customer group members --> 
     <xsl:apply-templates select="key('kCustomerGroup', $officeCode)" /> 
     </xsl:copy> 
    </xsl:for-each> 
    </xsl:if> 
</xsl:template> 

<!-- remove unwanted attribute with empty template -->  
<xsl:template match="Customer/@CodeModifier" /> 

retours

<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010"></Customer> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011"></Customer> 
    <Customer CustomerId="0012"></Customer> 
</Office> 
<Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010"></Customer> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011"></Customer> 
</Office> 

Remarque

  • L'utilisation de modes de modèle. J'ai fait un modèle spécifiquement pour le groupement Customer. Le traitement des noeuds normaux se produit dans le modèle d'identité.
  • L'utilisation d'une boucle à un seul nœud xsl:for-each pour modifier le nœud de contexte pour xsl:copy.
  • L'utilisation d'un modèle vide pour supprimer des nœuds de la sortie.
  • Que vous pouvez copier des attributs avec xsl:copy et encore écraser l'un d'entre eux plus tard.
  • La sortie est dans l'ordre des documents.

Pour forcer un ordre particulier, utiliser quelque chose comme ceci:

<xsl:apply-templates select="//Customer" mode="CustomerGroup"> 
    <xsl:sort select="../@Code" data-type="text" order="ascending" /> 
</xsl:apply-templates> 

et

<xsl:apply-templates select="key('kCustomerGroup', $officeCode)"> 
    <xsl:sort select="@CodeModifier" data-type="number" order="ascending" /> 
</xsl:apply-templates> 
1

Un XSLT complet et court 2.0 solution:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/*/*"> 
    <xsl:for-each-group select="Customer" group-by="@CodeModifier"> 
    <Office> 
    <xsl:copy-of select="../@*"/> 
    <xsl:attribute name="Code" select= 
     "concat(../@Code, current-grouping-key())"/> 
    <xsl:copy-of select="current-group()"/> 
    </Office> 
    </xsl:for-each-group> 
</xsl:template> 
</xsl:stylesheet> 

lorsqu'il est appliqué sur le document XML suivant (basé sur le fragment XML fourni et enveloppant tout en un élément supérieur unique pour en faire un document XML bien formée):

<company> 
    <Office Code="1" OtherAttribute="5"> 
     <Customer CustomerId="0010" CodeModifier="A"/> 
     <Customer CustomerId="0011" CodeModifier="B"/> 
     <Customer CustomerId="0012" CodeModifier="B"/> 
    </Office> 
    <Office Code="2" OtherAttribute="6"> 
     <Customer CustomerId="2010" CodeModifier="A"/> 
     <Customer CustomerId="0011" CodeModifier="C"/> 
    </Office> 
</company> 

produit le résultat souhaité, correct:

<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010" CodeModifier="A"/> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011" CodeModifier="B"/> 
    <Customer CustomerId="0012" CodeModifier="B"/> 
</Office> 
    <Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010" CodeModifier="A"/> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011" CodeModifier="C"/> 
</Office>