2010-02-17 4 views
4

J'ai une question. Je le fichier xml source suivante:Transformer la structure XML en une autre structure xml avec xslt

Source xml:

<Container> 
    <DataHeader> 
    <c id="b" value="TAG" /> 
    <c id="g" value="Info" /> 
    </DataHeader> 
    <Data> 
    <Rows> 
     <r no="1"> 
     <c id="b" value="uid1" uid="T.A.uid1" /> 
     <c id="g" value="uid1|tag1|attr1|somevalue1" /> 
     </r> 
    <r no="1"> 
     <c id="b" value="uid1" uid="T.A.uid1" /> 
     <c id="g" value="uid1|tag1|attr2|somevalue2" /> 
     </r> 
     <r no="2"> 
     <c id="b" value="uid1" uid="T.A.uid1" /> 
     <c id="g" value="uid1|tag2|attr3|somevalue3" /> 
     </r> 
    <r no="10"> 
     <c id="b" value="uid2" uid="T.A.uid2" /> 
     <c id="g" value="uid2|tag1|attr1|somevalue4" /> 
     </r> 
     <r no="11"> 
     <c id="b" value="uid2" uid="T.A.uid2" /> 
     <c id="g" value="uid2|tag2|attr3|somevalue5" /> 
     </r> 
    </Rows> 
    </Data> 
</Container> 

L'élément 'c' avec id 'g' est important dans le xml source. Ceci est une chaîne concaténée dont les valeurs sont séparées par un '| '. Nous avons besoin de ces valeurs pour faire le xml cible. L'élément 'c' avec l'ID 'b' vous pouvez utiliser pour séparer le 'uid'.

exemple et explantion des valeurs:

<c id="g" value="uid1|tag1|attr1|somevalue1" /> 
**uid value** | element node | **attribute** | attribute value 
**uid1** | tag1 | **attr1** |somevalue1 

éléments Al avec le même 'uid' doivent être regroupés en une seule élément "TestTag" (voir XML cible). Les attributs Al (attr1, attr2) ayant le même élément parent (par exemple 'tag1') doivent être ajoutés à 1 élément. Je ne peux utiliser que xslt (xpath) 1.0.

Le fichier xml cible devrait ressembler à ceci après la transformation.

xml cible après transformé par xsl:

<Container> 
<TestTag> 
    <object UID="T.A.uid1" Name="uid1"/> 
    <tag1 attr1="somevalue1" attr2="somevalue2"/> 
    <tag2 attr3="*somevalue3"/> 
</TestTag> 
<TestTag> 
    <Iobject UID="T.A.uid2" Name="uid2"/> 
    <tag1 attr1="somevalue4" /> 
    <tag2 attr3="somevalue5"/> 
</TestTag> 
</Container> 

Quelles sont les solutions possibles pour transformer xml source xml pour cibler? J'ai essayé plusieurs choses mais je suis coincé en ce moment.

+0

+1 Bonne première question. Bienvenue sur Stack Overflow! – Tomalak

Répondre

2

Ce n'est pas exactement difficile, mais est ahurissant en raison de vaste (mais nécessaire) l'utilisation imbriquée de substring-before() et substring-after().

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 
    <!-- index <c> nodes by their @id + "uid value" --> 
    <xsl:key name="kObject" match="r/c" use=" 
    concat(@id, '|', @value) 
    " /> 
    <!-- index <c> nodes by their @id + "uid value" --> 
    <xsl:key name="kTagByUid" match="r/c" use=" 
    concat(@id, '|', substring-before(@value, '|')) 
    " /> 
    <!-- index <c> nodes by their @id + "uid value" + "tag name" --> 
    <xsl:key name="kTagByName" match="r/c" use=" 
    concat(@id, '|', 
     substring-before(
     @value, 
     substring-after(substring-after(@value, '|'), '|') 
    ) 
    ) 
    " /> 

    <xsl:variable name="vTagId" select="/Container/DataHeader/c[@value='TAG'][1]/@id" /> 
    <xsl:variable name="vInfoId" select="/Container/DataHeader/c[@value='Info'][1]/@id" /> 

    <!-- processing starts here --> 
    <xsl:template match="Container"> 
    <xsl:copy> 
     <!-- apply templates to unique <c @id=$vTagId> tags --> 
     <xsl:apply-templates mode="tag" select=" 
     Data/Rows/r/c[@id=$vTagId][ 
      generate-id() 
      = 
      generate-id(key('kObject', concat(@id, '|', @value))[1]) 
     ] 
     " /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="c" mode="tag"> 
    <TestTag> 
     <object UID="{@uid}" name="{@value}" /> 
     <!-- apply templates to unique <c @id="g"> tags --> 
     <xsl:apply-templates mode="info" select=" 
     key('kTagByUid', concat($vInfoId, '|', @value))[ 
      generate-id() 
      = 
      generate-id(
      key(
       'kTagByName', 
       concat(@id, '|', 
       substring-before(
        @value, 
        substring-after(substring-after(@value, '|'), '|') 
       ) 
      ) 
      )[1] 
     ) 
     ] 
     " /> 
    </TestTag> 
    </xsl:template> 

    <xsl:template match="c" mode="info"> 
    <!-- select 'uid1|tag1|' - it's the key to kTagByName --> 
    <xsl:variable name="key" select="substring-before(@value, substring-after(substring-after(@value, '|'), '|'))" /> 
    <!-- select 'tag1' - it's the element name --> 
    <xsl:variable name="name" select="substring-before(substring-after($key, '|'), '|')" /> 

    <xsl:element name="{$name}"> 
     <xsl:for-each select="key('kTagByName', concat(@id, '|', $key))"> 
     <!-- select 'attr1|somevalue1' - it's the attribute definition --> 
     <xsl:variable name="attrDef" select="substring-after(@value, $key)" /> 
     <!-- create an attribute --> 
     <xsl:attribute name="{substring-before($attrDef, '|')}"> 
      <xsl:value-of select="substring-after($attrDef, '|')" /> 
     </xsl:attribute> 
     </xsl:for-each> 
    </xsl:element> 
    </xsl:template> 

</xsl:stylesheet> 

génère:

<Container> 
    <TestTag> 
    <object UID="T.A.uid1" name="uid1" /> 
    <tag1 attr1="somevalue1" attr2="somevalue2"></tag1> 
    <tag2 attr3="somevalue3"></tag2> 
    </TestTag> 
    <TestTag> 
    <object UID="T.A.uid2" name="uid2" /> 
    <tag1 attr1="somevalue4"></tag1> 
    <tag2 attr3="somevalue5"></tag2> 
    </TestTag> 
</Container> 

Notez que cela ne prête pas attention à dupliquer les définitions d'attributs. Si vous avez uid1|tag1|attr1|somevalue1 et plus tard uid1|tag1|attr1|othervalue1, vous finirez avec un attribut: attr1="othervalue1" car dans le <xsl:for-each> les deux obtiennent leur tour, et le dernier gagne (c'est-à-dire finit dans la sortie).

Il est possible de répondre à cela aussi, il faudrait une clé de plus et un groupement de plus Muenchian, je vais laisser cela comme un exercice pour le lecteur. Il h. ;)

+0

+1, belle réponse [15 caractères] –

+0

Bonjour Tomalak, Merci pour votre aide. Votre solution fonctionne très bien. Mais maintenant j'ai une autre question. Dans le fichier XML source, il y a également un élément DataHeader. Nous ne voulons pas faire correspondre la valeur de l'identificateur car cela est généré et l'identifiant et la valeur de la combinaison peuvent être différents à chaque fois. Notre solution était d'utiliser xsl: key pour obtenir l'ID par valeur. J'ai fait deux variables pour obtenir des ID ('b' et 'g') par valeur. Un exemple: TripleJ

+0

Le problème est que dans votre exemple votre correspondant sur id dans la clé xsl: et le modèle correspond et je ne peux pas faire usage d'une variable dans xsl: key ou dans le match de modèle. n'est pas possible. Comment pouvons-nous obtenir le même résultat en utilisant la solution ci-dessus ou devons-nous réécrire le xslt entier pour le faire fonctionner? – TripleJ

Questions connexes