Question

Je suis en train de rassembler un fichier XSL qui va créer un script de génération NAnt en utilisant comme entrée un fichier XML qui définit tous les éléments à construire. Nous avons beaucoup de projets très similaires avec des mises en page standard et des normes définies pour les zones de transfert. Avoir un fichier XML qui définit ce que les développeurs souhaitent faire plutôt que de décrire la manière dont cela doit être fait contribuerait grandement à l'adoption du service de construction.

Je veux définir très tôt dans le fichier XML de construction du produit les modes de construction à utiliser, c'est-à-dire.

<Build>
    <BuildModes>
        <Mode name="Debug" />
        <Mode name="Release" />
    </BuildModes>

    <ItemsToBuild>
        <Item name="first item" .... />
        <Item name="second item" .... />
    </ItemsToBuild>
 </Build>

Je veux avoir un

<xsl:for-each select="/Build/BuildModes/Mode">
    <xsl:for-each select="/Build/ItemsToBuild/Item">
        <exec program="devenv">
        <xsl:attribute name="line">
            use the @name from the Mode and other stuff from Item to build up the command line
        </xsl:attribute>
    </xsl:for-each>
</xsl:for-each>

Maintenant, je peux le faire en définissant une valeur entre les deux lignes for each pour contenir la valeur Mode / @ name, mais c'est un peu compliqué, et ce que je veux réellement faire, c'est retourner le suivant pour que la construction Le mode est à l'intérieur de la boucle d'élément afin qu'il construise un mode puis l'autre. Pour le moment, il construirait tout le débogage, puis toutes les versions de la version. Pour ce faire, je devrais en avoir plusieurs déclarés et cela devient très compliqué.

Il est donc imbriqué lorsque les éléments du document source ne sont pas imbriqués.

EDIT:

ok, comme le montre la réponse acceptée ci-dessous, utiliser pour-each est une mauvaise idée dans la plupart des cas, et j’ai retravaillé cet exemple. C'est un peu différent car le schéma que j'utilise a été simplifié pour le post ci-dessus, mais vous en avez l'idée.

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

<xsl:template match="/BuildDefinition">
    <xsl:apply-templates select="/BuildDefinition/VS2008SLN/DeploymentProject"/>
</xsl:template>

<xsl:template match="/BuildDefinition/VS2008SLN/DeploymentProject">
    <xsl:apply-templates select="/BuildDefinition/BuildModes/Mode">
        <xsl:with-param name="BuildTarget" select="." />
    </xsl:apply-templates>
</xsl:template>

<xsl:template match="/BuildDefinition/BuildModes/Mode">
    <xsl:param name="BuildTarget" />
    <exec program="devenv"> <!-- not the real call, but for example purposes -->
        <xsl:attribute name="ProjectName" select="$BuildTarget/@ProjectName"/>
        <xsl:attribute name="SolutionName" select="$BuildTarget/../@SolutionName" />
        <xsl:attribute name="ModeName" select="@name"/>
    </exec>
</xsl:template>
</xsl:stylesheet>

et c'est le schéma sur lequel il s'exécute

<BuildDefinition Version="1.0">

 <BuildModes>
    <Mode name="Debug" />
    <Mode name="Release" />
</BuildModes>

<VS2008SLN 
    SolutionName="MySolution"
    SolutionDirectory="Visual Studio 2008\MySolution">
    <DeploymentProject 
        ProjectName="MyDeploymentProject" 
        DeploymentTargetDirectory="EndsUpHere"
        DeploymentManifestName="AndCalledThisInTheDocumentation" />
</VS2008SLN>
Était-ce utile?

La solution

La clé du succès est de ne pas utiliser < code> < xsl: pour chaque > du tout .

<xsl:template match="/">
  <xsl:apply-templates select="Build/BuildModes/Mode" />
</xsl:template>

<xsl:template match="Build/BuildModes/Mode">
  <exec program="devenv">
    <xsl:apply-templates select="/Build/ItemsToBuild/Item">
      <xsl:with-param name="BuildMode" select="." />
    </xsl:apply-templates>
  </exec>
</xsl:template>

<xsl:template match="Build/ItemsToBuild/Item">
  <xsl:param name="BuildMode" />
  <xsl:attribute name="line">
    <!-- use $BuildMode/@name etc. to build up the command line -->
  </xsl:attribute>
</xsl:template>

Autres conseils

Je pense que la technique clé qui vous manque probablement est la sauvegarde du nœud de contexte actuel dans une variable avant de faire quelque chose qui modifie les nœuds de contexte. Ce que vous avez peut être fait pour fonctionner si vous utilisez cette technique, que l'exemple ci-dessous utilise.

Comme beaucoup de problèmes XSLT, il est plus facile à résoudre si vous pensez aux transformations plutôt qu'aux procédures. La question n'est pas vraiment "comment imbriquer des boucles pour chaque-chacune?", C'est "comment transformer des éléments Item en éléments exec désirés?" ;

<xsl:template match="/">
   <output>
      <xsl:apply-templates select="/Build/ItemsToBuild/Item"/>
   </output>
</xsl:template>

<xsl:template match="Item">
   <xsl:variable name="item" select="."/>
   <xsl:for-each select="/Build/BuildModes/Mode">
      <exec program="devenv">
         <xsl:attribute name="itemName" select="$item/@name"/>
         <xsl:attribute name="modeName" select="@name"/>
         <!-- and so on -->
      </exec>
   </xsl:for-each>
</xsl:template>

Vous pouvez utiliser un modèle nommé:

<xsl:template name="execute">
  <xsl:param name="mode" />
  <xsl:for-each select="/Build/ItemsToBuild/Item">
   <exec program="devenv">
    <xsl:attribute name="line">
        use $mode and other stuff from Item to build up the command line
    </xsl:attribute>
   </exec>
  </xsl:for-each>
</xsl:template>

puis appelez-le:

<xsl:for-each select="/Build/BuildModes/Mode">
 <xsl:call-template name="execute">
  <xsl:with-param name="mode" select="@name" />
 </xsl:call-template>
</xsl:for-each>

Cela aidera à séparer les choses, mais je ne suis pas sûr que ce soit vraiment plus clair.

Malheureusement, peu importe votre façon de voir les choses, vous devrez faire de la plomberie, car vous essayez d'obtenir deux contextes en même temps.

Vous pouvez utiliser une variable pour stocker le contexte de l'élément. Il existe également un raccourci pour nettoyer les définitions d'attributs .

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

    <xsl:template match="/">
        <someroot>
            <xsl:for-each select="/Build/ItemsToBuild/Item">
                <xsl:variable name="item" select="." />
                <xsl:for-each select="/Build/BuildModes/Mode">
                    <exec program="devenv"
                        item="{$item/@name}" line="{@name}" />
                </xsl:for-each>
            </xsl:for-each>
        </someroot>
    </xsl:template>
</xsl:stylesheet>

Le résultat est

<someroot>
    <exec program="devenv" item="item1" line="Debug" />
    <exec program="devenv" item="item1" line="Release" />
    <exec program="devenv" item="item2" line="Debug" />
    <exec program="devenv" item="item2" line="Release" />
</someroot>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top