XSLT:在该循环外部的循环中使用表达式的结果

时间:2009-10-20 09:58:12

标签: variables xslt scope loops

我正在尝试将XML文件的某些部分转换为另一个XML文件。源文件:

<CUSTOMERS>
  <CUSTOMER>
    <CUSTOMER_NUMBER>12345678</CUSTOMER_NUMBER>
    <CUSTOMER_ADDRESS>
      <CUSTOMER_ADDRESS_NAME>John Doe</CUSTOMER_ADDRESS_NAME>
      <CUSTOMER_ADDRESS_STREET>Street 1</CUSTOMER_ADDRESS_STREET>
      <CUSTOMER_ADDRESS_CITY>Amsterdam</CUSTOMER_ADDRESS_CITY>
    </CUSTOMER_ADDRESS>
    <CUSTOM_FIELDS>
      <CUSTOM_FIELD>
        <CUSTOM_FIELD_NAME>Cellphone</CUSTOM_FIELD_NAME>
        <CUSTOM_FIELD_VALUE>443209432</CUSTOM_FIELD_VALUE>
      </CUSTOM_FIELD>
      <CUSTOM_FIELD>
        <CUSTOM_FIELD_NAME>Geo</CUSTOM_FIELD_NAME>
        <CUSTOM_FIELD_VALUE>323932121,31231233,0</CUSTOM_FIELD_VALUE>
      </CUSTOM_FIELD>
    </CUSTOM_FIELDS>
  </CUSTOMER>
</CUSTOMERS>

规则:

  • 并非每个CUSTOMER都有CUSTOM_FIELDS,那些没有CUSTOM_FIELDS的人不应该被处理。
  • CUSTOMERCUSTOM_FIELDS时,所有CUSTOM_FIELDS的数量和顺序可能会有所不同。
  • CUSTOMER只有在CUSTOM_FIELD名称为'Geo'
  • 的情况下才能处理

因此,在我的XSL中,我试图遍历所有CUSTOMERS,并通过CUSTOMER遍历每个CUSTOM_FIELDS循环。当它找到一个名为'Geo'的人时,它应该输出数据。

<xsl:for-each select="CUSTOMERS/CUSTOMER">
  <xsl:for-each select="CUSTOM_FIELDS/CUSTOM_FIELD">
    <xsl:if test=".[CUSTOM_FIELD_NAME='Geo']">
      <Placemark>
        <name>
          <xsl:value-of select="CUSTOMER_NUMBER" />
          <xsl:text> </xsl:text>
          <xsl:value-of select="CUSTOMER_ADDRESS/CUSTOMER_ADDRESS_NAME" />
        </name>
        <styleUrl>#msn_ylw-pushpin</styleUrl>
        <Point>
          <coordinates>
            <xsl:value-of select="CUSTOM_FIELD_NAME" />
          </coordinates>
        </Point>
      </Placemark>
    </xsl:if>
  </xsl:for-each>
</xsl:for-each>

但当然,此时我在CUSTOMERS/CUSTOMER/CUSTOM_FIELDS/CUSTOM_FIELD,因此无法从几级以上的节点输出数据。

我已经尝试在if-block中设置一个变量,然后应该在if-block之后读取以查看是否应该返回任何数据但是我理解变量只能设置一次因此它们不能用于这个目标要么。

所以我的问题是这些:

  • 我是否真的需要一个循环来查看CUSTOM_FIELD是谁CUSTOM_FIELD_NAME = 'Geo'
  • 如果需要循环,我如何在循环内部使用循环内部的结果?
  • 或者,如果那是不可能的,我怎样才能从比我目前所在节点高几级的节点返回值?

1 个答案:

答案 0 :(得分:2)

使用XPath可以轻松解决您的问题。考虑这个解决方案:

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

  <xsl:template match="CUSTOMERS">
    <Placemarks>
      <!-- apply templates to every customer -->
      <xsl:apply-templates select="CUSTOMER" />
    </Placemarks>
  </xsl:template>

  <xsl:template match="CUSTOMER">  
    <!-- process only customers that have a "Geo" custom field -->
    <xsl:if test="CUSTOM_FIELDS/CUSTOM_FIELD[CUSTOM_FIELD_NAME = 'Geo']">
      <Placemark>
        <name>
          <xsl:value-of select="CUSTOMER_NUMBER"/>
          <xsl:text> </xsl:text>
          <xsl:value-of select="CUSTOMER_ADDRESS/CUSTOMER_ADDRESS_NAME"/>
        </name>
        <styleUrl>
          <xsl:text>#msn_ylw-pushpin</xsl:text>
        </styleUrl>
        <Point>
          <coordinates>
            <xsl:value-of select="
              CUSTOM_FIELDS/CUSTOM_FIELD[
                CUSTOM_FIELD_NAME = 'Geo'
              ]/CUSTOM_FIELD_VALUE
            "/>
          </coordinates>
        </Point>
      </Placemark>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

第一个模板(match="CUSTOMERS")处理每个<CUSTOMER>节点。

将它们移交给第二个模板(match="CUSTOMER"),该模板决定是否处理客户。

通过检查是否存在名为'Geo'的自定义字段来实现此目的。如果存在此类节点,则会创建<Placemark>。以上的输出是:

<Placemarks>
  <Placemark>
    <name>12345678 John Doe</name>
    <styleUrl>#msn_ylw-pushpin</styleUrl>
    <Point>
      <coordinates>323932121,31231233,0</coordinates>
    </Point>
  </Placemark>
</Placemarks>

因此不需要任何循环或大量检查,它们表明您没有从XSLT角度来看问题。 ; - )

现在回答你的问题:

  

[...]因此无法输出   来自少数节点的数据   升级。

当然是 - 您可以随时使用'..' XPath运算符,或沿ancestor::轴移动。我建议你先阅读一些关于XPath的内容,你似乎错过了很多。

  

我已经尝试在中设置变量   if-block,然后应该读取   if-block查看是否有任何数据   被退回,但我理解变量   只能设置一次,所以不能   用于此目标。

变量不仅是只读的,如果处理流程离开其父元素,它们也会立即超出范围。因此,从外部检查if-block中的 内部的变量是不可能的。

  

我真的需要一个循环来查看是否   有一个CUSTOM_FIELD谁   CUSTOM_FIELD_NAME = 'Geo'

没有。您需要如上所示的XPath检查。

  

如果需要循环,我该如何使用?   从环内部,外部产生   循环?

没有。如上所述。

  

如果那是不可能的,我该怎么办?   返回来自a的节点的值   我节点以上的几个级别   目前在?

上面也解释过。 ; - )