Как мне вложить xsl: for-each из разных частей документа xml? - PullRequest
0 голосов
/ 15 марта 2009

Я собираю XSL вместе, чем создам скрипт сборки NAnt, используя в качестве входных данных XML-файл, который определяет все элементы, которые должны быть собраны. У нас есть много очень похожих проектов со стандартными макетами и определенными стандартами для областей хэндовера, и поэтому наличие файла XML, который определяет, чего хотят разработчики, а не описывает, как это должно быть сделано, очень помогло бы освоению сервиса сборки.

Я хочу заранее определить в XML-файле сборки продукта используемые режимы сборки, т. Е.

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

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

Я хочу иметь

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

Теперь я могу сделать это, задав между двумя строками for-each значение, определенное для значения Mode / @ name, но это немного запутанно, и я действительно хочу перевернуть nexting так, чтобы сборка mode находится внутри цикла Item, поэтому он создает один режим, а затем другой. На данный момент он собрал бы все отладки, а затем все сборки выпуска. Чтобы сделать это, мне нужно было бы объявить несколько, и это становится очень грязным.

Так что он вложен, когда элементы в исходном документе не вложены.

EDIT:

хорошо, поскольку принятый ответ ниже показывает, что использование for-each является плохой идеей в большинстве случаев, и я переделал этот пример в следующий. Это немного отличается, поскольку схема, которую я использую, была упрощена для вышеупомянутого поста, но вы поняли идею.

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

и это схема, к которой он работает

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

Ответы [ 4 ]

7 голосов
/ 15 марта 2009

Ключ к успеху - не использовать <xsl:for-each> вообще .

<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>
2 голосов
/ 15 марта 2009

Я думаю, что ключевая техника, которую вы, вероятно, упускаете, - это сохранение текущего узла контекста в переменной перед тем, как что-то изменить узлы контекста. То, что у вас есть, можно заставить работать, если вы используете эту технику, которую использует пример ниже.

Как и многие проблемы XSLT, это легче решить, если вы думаете о преобразованиях вместо процедур. Вопрос не в том, «как я вкладываю в каждый цикл?», А в том, «как мне преобразовать Item элементы в нужные exec элементы?»

<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>
1 голос
/ 15 марта 2009

Вы можете использовать именованный шаблон:

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

тогда назовите это:

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

Это поможет разделить вещи, но я не уверен, что это действительно более понятно.

К сожалению, независимо от того, как вы на это смотрите, вам придётся сделать немного сантехники, поскольку вы пытаетесь получить два контекста одновременно.

0 голосов
/ 15 марта 2009

Вы можете использовать переменную для хранения контекста элемента. Также имеется сокращение для очистки определений атрибутов .

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

Результат

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