XSLT选择当前低于当前的特定节点

时间:2012-07-18 08:17:35

标签: xslt

我正试图为其他人解决这个问题并且自己遇到了一个问题。

我有XML:

<Process>
  <name>Pro1</name>
  <duration>Dur1</duration>
  <time>Time1</time>
  <name>Pro2</name>
  <duration>Dur2</duration>
  <time>Time2</time>
  <name>Pro3</name>
  <duration>Dur3</duration>
  <time>Time3</time>
  <name>Pro4</name>
  <duration>Dur4</duration>
  <time>Time4</time>
  <name>Pro5</name>
  <duration>Dur5</duration>
  <time>Time5</time>
</Process>

输出:

<Process>
  <Process_Info>
    <name>Pro1</name>
    <duration>Dur1</duration>
    <time>Time1</time>
  </Process_Info>
  <Process_Info>
    <name>Pro2</name>
    <duration>Dur2</duration>
    <time>Time2</time>
  </Process_Info>
  <Process_Info>
    <name>Pro3</name>
    <duration>Dur3</duration>
    <time>Time3</time>
  </Process_Info>
  <Process_Info>
    <name>Pro4</name>
    <duration>Dur4</duration>
    <time>Time4</time>
  </Process_Info>
  <Process_Info>
    <name>Pro5</name>
    <duration>Dur5</duration>
    <time>Time5</time>
  </Process_Info>
</Process>

使用XSLT:

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

  <xsl:variable name ="varProcess" select ="Process"/>

  <xsl:template match="Process">
    <xsl:element name="Process">
      <xsl:for-each select ="name">
        <xsl:variable name ="posName" select ="position()"/>
        <xsl:element name ="Process_Info">
          <xsl:copy-of select ="."/>
          <xsl:copy-of select="$varProcess/duration[$posName]"/>
          <xsl:copy-of select="$varProcess/time[$posName]"/>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

但是,<duration><time>节点并不总是存在,<name>是唯一的保护节点。因此,如果遗失了一个,position()选择失败。

即使<duration>和/或<time>不存在,如何更改XSLT以使其正常工作。

我的理论是你选择当前名称节点下面的两个节点,如果它们是<duration><time>,它们会被复制?但不确定如何实现。

导致问题的当前输出示例。

输入:

<Process>
  <name>Pro1</name>
  <duration>Dur1</duration>
  <time>Time1</time>
  <name>Pro2</name>
  <duration>Dur2</duration>
  <time>Time2</time>
  <name>Pro3</name>
  <duration>Dur3</duration>
  <time>Time3</time>
  <name>Pro4</name>
  <time>Time4</time>
  <name>Pro5</name>
  <duration>Dur5</duration>
</Process>

输出:

<Process>
  <Process_Info>
    <name>Pro1</name>
    <duration>Dur1</duration>
    <time>Time1</time>
  </Process_Info>
  <Process_Info>
    <name>Pro2</name>
    <duration>Dur2</duration>
    <time>Time2</time>
  </Process_Info>
  <Process_Info>
    <name>Pro3</name>
    <duration>Dur3</duration>
    <time>Time3</time>
  </Process_Info>
  <Process_Info>
    <name>Pro4</name>
    <duration>Dur5</duration> <!-- Should be in the below process_info -->
    <time>Time4</time>
  </Process_Info>
  <Process_Info>
    <name>Pro5</name>
  </Process_Info>
</Process>

3 个答案:

答案 0 :(得分:2)

这就是诀窍,虽然方法中有点“手动”。可能有更优雅的方法来实现这个目标

<xsl:template match="Process">
    <xsl:element name="Process">
        <xsl:for-each select ="name">
            <xsl:element name ="Process_Info">
                <xsl:copy-of select ="."/>
                <xsl:variable name="firstSib" select="local-name(following-sibling::*[1])" />
                <xsl:variable name="secondSib" select="local-name(following-sibling::*[2])" />
                <xsl:choose>
                    <xsl:when test="$firstSib='duration'">
                        <xsl:copy-of select="following-sibling::*[1]"/>
                        <xsl:choose>
                            <xsl:when test="$secondSib='time'">
                                <xsl:copy-of select="following-sibling::*[2]"/>
                            </xsl:when>
                            <xsl:otherwise>
                                <time>SomeDefaultValueForMissingTime</time>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:when>
                    <xsl:when test="$firstSib='time'">
                        <duration>SomeDefaultValueForMissingDuration</duration>
                        <xsl:copy-of select="following-sibling::*[1]"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <duration>SomeDefaultValueForMissingDuration</duration>
                        <time>SomeDefaultValueForMissingTime</time>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:element>
        </xsl:for-each>
    </xsl:element>
</xsl:template>

输入:

<Process>
    <name>Pro1</name>
    <duration>Dur1</duration>
    <time>Time1</time>
    <name>NameMissingDuration</name>
    <time>TimeMissingDuration</time>
    <name>NameMissingTime</name>
    <duration>DurMissingTime</duration>
    <name>NameMissingBoth</name>
    <name>NormalName</name>
    <duration>NormalDuration</duration>
    <time>NormalTime</time>
</Process>

<强>输出

<Process>
  <Process_Info>
    <name>Pro1</name>
    <duration>Dur1</duration>
    <time>Time1</time>
  </Process_Info>
  <Process_Info>
    <name>NameMissingDuration</name>
    <duration>SomeDefaultValueForMissingDuration</duration>
    <time>TimeMissingDuration</time>
  </Process_Info>
  <Process_Info>
    <name>NameMissingTime</name>
    <duration>DurMissingTime</duration>
    <time>SomeDefaultValueForMissingTime</time>
  </Process_Info>
  <Process_Info>
    <name>NameMissingBoth</name>
    <duration>SomeDefaultValueForMissingDuration</duration>
    <time>SomeDefaultValueForMissingTime</time>
  </Process_Info>
  <Process_Info>
    <name>NormalName</name>
    <duration>NormalDuration</duration>
    <time>NormalTime</time>
  </Process_Info>
</Process>

答案 1 :(得分:2)

这是一个没有xsl:if的替代解决方案。

这演示了在选择第一个时间(或 duration )元素时使用generate-id()函数测试节点相等性,该元素遵循当前的名称元素,其第一个名称元素是当前元素。

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

  <xsl:template match="Process">
    <xsl:copy>
      <xsl:apply-templates select="name"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="name">
    <xsl:variable name="this" select="generate-id()"/>
    <Process_Info>
      <xsl:copy-of select="."/>
      <xsl:copy-of select="following-sibling::duration
                           [generate-id(preceding-sibling::name[1]) = $this]
                           [1]"/>
      <xsl:copy-of select="following-sibling::time
                           [generate-id(preceding-sibling::name[1]) = $this]
                           [1]"/>
    </Process_Info>
  </xsl:template>

</xsl:stylesheet>

答案 2 :(得分:1)

根据nonnb的反应,所有信用都归他所有,最终的XSLT正在被使用。我的错误是不包括我不需要填充节点的问题。

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

  <xsl:template match="Process">
    <xsl:element name="Process">
      <xsl:for-each select ="name">
        <xsl:element name ="Process_Info">
          <xsl:copy-of select ="."/>
          <xsl:variable name="firstSib" select="local-name(following-sibling::*[1])" />
          <xsl:variable name="secondSib" select="local-name(following-sibling::*[2])" />
          <xsl:if test ="($firstSib='duration') or ($firstSib='time')">
            <xsl:copy-of select="following-sibling::*[1]"/>
          </xsl:if>
          <xsl:if test ="($secondSib='duration') or ($secondSib='time')">
            <xsl:copy-of select="following-sibling::*[2]"/>
          </xsl:if>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>