识别上下文节点在另一个节点集中的位置

时间:2012-05-25 09:58:28

标签: xml xslt xpath

我有一个XML文档,如下所示:

<Root>
    <Cover>
        <Cover_Ref Val="1"/>
        <Cover_Code Val="Food in transit"/>
        <AdditionalCover>
            <AdditionalCover_Area Val="Sussex"/>
        </AdditionalCover>
    </Cover>
    <Cover>
        <Cover_Ref Val="2"/>
        <Cover_Code Val="Frozen goods"/>
    </Cover>
    <Cover>
        <Cover_Ref Val="3"/>
        <Cover_Code Val="Business equipment"/>
        <AdditionalCover>
            <AdditionalCover_Area Val="Sussex"/>
        </AdditionalCover>
    </Cover>
    <AdditionalCoverResult>
        <CoverArea Val="Sussex"/>
        <CoverExcluded Val="N"/>
    </AdditionalCoverResult>
    <AdditionalCoverResult>
        <CoverArea Val="Sussex"/>
        <CoverExcluded Val="Y"/>
    </AdditionalCoverResult>
</Root>

我需要把它变成这样的东西:

<Insurances>
    <Insurance>
        <reference>1</reference>
        <CoverType>Food in transit</CoverType>
        <Region>Sussex</Region>
        <Excluded>N</Excluded>
    </Insurance>
    <Insurance>
        <reference>2</reference>
        <CoverType>Frozen goods</CoverType>
    </Insurance>
    <Insurance>
        <reference>3</reference>
        <CoverType>Business equipment</CoverType>
        <Region>Sussex</Region>
        <Excluded>Y</Excluded>
    </Insurance>
</Insurances>

AdditionalCoverResult元素引用包含AdditionalCover元素的Cover元素 - 因此第一个Cover元素的封面不排除,而第三个Cover元素的封面不包括在内。第二个封面元素没有AdditionalCover,因此没有引用它的AdditionalCoverResult元素。

我的问题是在<xsl:for-each select="//Cover">循环内我无法想出一种识别我应该寻找哪个AdditionalCoverResult元素的方法。在for-each中使用position()最终导致我错误地输出了没有AdditionalCover的Cover元素的AdditionalCoverResult信息。

我必须保留Cover的原始顺序,所以我不能做两个单独的循环,一个用于Cover with AdditionalCover,一个用于Cover而不用AdditionalCover。

//Cover返回的节点集中,有一种方法可以识别上下文节点在//Cover[AdditionalCover]返回的节点集中的位置吗?

我有点猜测,对于Cover元素,我需要思考“我在//Cover[AdditionalCover]内的索引是什么,以便我可以使用该索引来识别我对应的AdditionalCoverResult元素?”

3 个答案:

答案 0 :(得分:1)

此转化

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

 <xsl:template match="/*">
  <Insurances>
   <xsl:apply-templates select="Cover"/>
  </Insurances>
 </xsl:template>

 <xsl:template match="Cover">
  <Insurance>
   <xsl:apply-templates/>
  </Insurance>
 </xsl:template>

 <xsl:template match="Cover_Ref">
  <reference><xsl:value-of select="@Val"/></reference>
 </xsl:template>

 <xsl:template match="Cover_Code">
  <CoverType><xsl:value-of select="@Val"/></CoverType>
 </xsl:template>

 <xsl:template match="AdditionalCover_Area">
  <Region><xsl:value-of select="@Val"/></Region>

  <Excluded>
    <xsl:value-of select=
    "/*/AdditionalCoverResult[CoverArea/@Val=current()/@Val]
             [position()
             =
              1 +count(current()
                       /ancestor::Cover[1]
                          /preceding-sibling::Cover
                            [AdditionalCover
                              /AdditionalCover_Area/@Val
                            =
                             current()/@Val
                            ]
                      )
              ]
               /CoverExcluded/@Val
    "/>
  </Excluded>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档时:

<Root>
    <Cover>
        <Cover_Ref Val="1"/>
        <Cover_Code Val="Food in transit"/>
        <AdditionalCover>
            <AdditionalCover_Area Val="Sussex"/>
        </AdditionalCover>
    </Cover>
    <Cover>
        <Cover_Ref Val="2"/>
        <Cover_Code Val="Frozen goods"/>
    </Cover>
    <Cover>
        <Cover_Ref Val="3"/>
        <Cover_Code Val="Business equipment"/>
        <AdditionalCover>
            <AdditionalCover_Area Val="Sussex"/>
        </AdditionalCover>
    </Cover>
    <AdditionalCoverResult>
        <CoverArea Val="Sussex"/>
        <CoverExcluded Val="N"/>
    </AdditionalCoverResult>
    <AdditionalCoverResult>
        <CoverArea Val="Sussex"/>
        <CoverExcluded Val="Y"/>
    </AdditionalCoverResult>
</Root>

生成想要的正确结果

<Insurances>
   <Insurance>
      <reference>1</reference>
      <CoverType>Food in transit</CoverType>
      <Region>Sussex</Region>
      <Excluded>N</Excluded>
   </Insurance>
   <Insurance>
      <reference>2</reference>
      <CoverType>Frozen goods</CoverType>
   </Insurance>
   <Insurance>
      <reference>3</reference>
      <CoverType>Business equipment</CoverType>
      <Region>Sussex</Region>
      <Excluded>Y</Excluded>
   </Insurance>
</Insurances>

答案 1 :(得分:0)

您可以将您感兴趣的当前上下文的一部分存储在变量中 - 例如:

<xsl:for-each select="//Cover">

  ...

  <xsl:variable name="additional" select="AdditionalCover/AdditionalCover_Area/@Val"/>
  <xsl:if select="$additional">
    <Excluded>
      <xsl:value-of select="AdditionalCoverResult[CoverArea/@Val = $additional]/CoverExcluded/@Val"/>
    </Excluded>
  </xsl:if>
</xs:for-each>

答案 2 :(得分:0)

发布问题后,我在相关部分看到了一个链接 - xpath/xslt to determine index of context node relative to all nodes of same name?

Dimitre Novatchev的回答表明,使用

count(preceding::question)+1

您可以找到当前上下文节点相对于文档中所有question元素的索引。

对于我的问题,我使用了count(preceding::Cover[AdditionalCover])+1并且它有效 - 我希望我能正确理解它,但它确实可以解决这个问题。

我不会立即选择这个作为正确的答案,因为如果有人有任何其他方法可以做到这一点我会非常感兴趣 - 我可能是XSLT的中级水平,这是一个学习更多关于它!