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

StackOverflow https://stackoverflow.com/questions/648363

Вопрос

Я собираю 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 так, чтобы режим сборки находился внутри цикла элементов, чтобы он создавал один режим, затем другой.На данный момент он будет создавать все сборки debug, а затем все сборки release.Чтобы сделать это, мне пришлось бы объявить несколько, и это становится очень запутанным.

Таким образом, он является вложенным, когда элементы в исходном документе не являются вложенными.

Редактировать:

хорошо, как показывает приведенный ниже принятый ответ, использование 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>
Это было полезно?

Решение

Ключом к успеху является не использовать <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>

Другие советы

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

Как и многие проблемы 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>

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

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

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

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

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

<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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top