for-each 并使用 xslt 对 xml 进行排序和分组

Posted

技术标签:

【中文标题】for-each 并使用 xslt 对 xml 进行排序和分组【英文标题】:for-each and sort and group the xml using xslt 【发布时间】:2013-03-07 07:21:47 【问题描述】:

我有一个如下的 XML。对于每个测试,我需要对 cat 和 title 和 group 进行排序。 对于 cat 值 abc,我需要获取标题并设置值。 任何集合值都符合在集合节点内按升序添加标题值的需要。在这里,我确实对 cat 值进行了硬编码。但在我的场景中,cat 值是从外部源获取的。

<?xml version="1.0" encoding="utf-8" ?>
<compound>
  <test>
    <title>k</title>
    <cetval>
      <cat>abc</cat>
      <cat>fcg</cat>
    </cetval>
    <value>1</value>
    <set>
      <color>blue</color>
    </set>
  </test>
  <test>
    <title>p</title>
    <cetval>
      <cat>fcg</cat>
      <cat>klm</cat>
    </cetval>

    <value>10</value>
    <set>
      <color>pink</color>
    </set>
  </test>
  <test>
    <title>j</title>
    <cetval>
      <cat>fcg</cat>
      <cat>klm</cat>
      <cat>abc</cat>
    </cetval>
    <value>484</value>
    <set>
      <color>yellow</color>
    </set>
  </test>
  <test>
    <title>v</title>
    <cetval>
      <cat>dji</cat>
      <cat>kfjsdlk</cat>
    </cetval>
    <value>93</value>

  </test>
  <test>
    <title>r</title>
    <cetval>
      <cat>deiu</cat>
      <cat>kfjdf</cat>
      <cat>abc</cat>
    </cetval>
    <value>10</value>
    <set>
      <color>blue</color>
    </set>
    <test>
      <title>i</title>
      <cetval>
        <cat>fcg</cat>
        <cat>klm</cat>
        <cat>abc</cat>
      </cetval>

      <value>10</value>

    </test>
    <test>
      <title>w</title>
      <cetval>
        <cat>diweif</cat>
        <cat>fjf</cat>
        <cat>abc</cat>
      </cetval>
      <value>10</value>
      <set>
        <color>yellow</color>
      </set>
    </test>
  </test>

</compound>    

我需要如下输出:

<?xml version="1.0" encoding="utf-8" ?>
<compound>
  <cat>abc</cat>
  <set>
    <color>blue</color>
    <title>k</title>
    <title>r</title>
  </set>
  <set>
    <color>yellow</color>
    <title>j</title>
    <title>w</title>
  </set>
  <title>i</title>


</compound>

我的变换如下所示:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">
      <xsl:for-each select="compound/test">
        <xsl:sort select="./cetval/cat" order="ascending"/>
        <xsl:sort select="./title" order="ascending"/>
        <xsl:for-each select="./cetval/cat">
          <xsl:choose>
            <xsl:when test="./cat = 'abc' > 0">

              <xsl:value-of select="ancestor::test/title"/>

            </xsl:when>
          </xsl:choose>
        </xsl:for-each>

        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

谁能建议我如何生成所需的输出?

【问题讨论】:

“蓝色”不应该有“k”和“r”的标题,而不是“k”和“p”吗? 蒂姆,我已经编辑了我的输出.. 选择&lt;title&gt;i&lt;/title&gt;的标准是什么? 如果 cat=abc 我需要选择标题... 【参考方案1】:

好的,我想我已经理解了您的要求。怎么样:

<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:key name="kColor" match="test" use="set/color" />
  <xsl:param name="catValue" select="'abc'" />

  <xsl:template match="/*">
    <xsl:copy>
      <cat>
        <xsl:value-of select ="$catValue"/>
      </cat>
      <xsl:apply-templates 
        select="//test[generate-id() = 
                       generate-id(
                            key('kColor', set/color)
                                  [cetval/cat = $catValue][1]
                                  )
                      ]" />
      <xsl:apply-templates 
        select="//test[cetval/cat = $catValue and not(set/color)]
                      /title">
        <xsl:sort select="."/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="test">
    <set>
      <xsl:apply-templates select="set/color" />
      <xsl:apply-templates
        select="key('kColor', set/color)[cetval/cat = $catValue]/title">
        <xsl:sort select="." />
      </xsl:apply-templates>
    </set>
  </xsl:template>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

这里$catValue 被指定为参数,因此您可以从外部传入不同的值。在您的示例 XML 上运行时,结果是:

<compound>
  <cat>abc</cat>
  <set>
    <color>blue</color>
    <title>k</title>
    <title>r</title>
  </set>
  <set>
    <color>yellow</color>
    <title>j</title>
    <title>w</title>
  </set>
  <title>i</title>
</compound>

【讨论】:

@user1490088:然后将其标记为正确答案(单击答案左侧的复选标记)【参考方案2】:

更短更简单

<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:key name="kTestByCat" match="test" use="cetval/cat"/>
 <xsl:key name="kTestByColor" match="test[cetval/cat='abc']"
      use="set/color"/>

 <xsl:template match="/">
  <compound>
    <cat>abc</cat>
      <xsl:apply-templates select=
      "/*/*[generate-id()
           =
            generate-id(key('kTestByColor',set/color)[1])]"/>
  </compound>
 </xsl:template>

 <xsl:template match="test">
  <set>
   <color><xsl:value-of select="set/color"/></color>
   <xsl:copy-of select="key('kTestByColor',set/color)/title"/>
  </set>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<compound>
    <test>
        <title>k</title>
        <cetval>
            <cat>abc</cat>
            <cat>fcg</cat>
        </cetval>
        <value>1</value>
        <set>
            <color>blue</color>
        </set>
    </test>
    <test>
        <title>p</title>
        <cetval>
            <cat>fcg</cat>
            <cat>klm</cat>
        </cetval>
        <value>10</value>
        <set>
            <color>pink</color>
        </set>
    </test>
    <test>
        <title>j</title>
        <cetval>
            <cat>fcg</cat>
            <cat>klm</cat>
            <cat>abc</cat>
        </cetval>
        <value>484</value>
        <set>
            <color>yellow</color>
        </set>
    </test>
    <test>
        <title>v</title>
        <cetval>
            <cat>dji</cat>
            <cat>kfjsdlk</cat>
        </cetval>
        <value>93</value>
    </test>
    <test>
        <title>r</title>
        <cetval>
            <cat>deiu</cat>
            <cat>kfjdf</cat>
            <cat>abc</cat>
        </cetval>
        <value>10</value>
        <set>
            <color>blue</color>
        </set>
        <test>
            <title>i</title>
            <cetval>
                <cat>fcg</cat>
                <cat>klm</cat>
                <cat>abc</cat>
            </cetval>
            <value>10</value>
        </test>
        <test>
            <title>w</title>
            <cetval>
                <cat>diweif</cat>
                <cat>fjf</cat>
                <cat>abc</cat>
            </cetval>
            <value>10</value>
            <set>
                <color>yellow</color>
            </set>
        </test>
    </test>
</compound>

产生了想要的正确结果:

<compound>
   <cat>abc</cat>
   <set>
      <color>blue</color>
      <title>k</title>
      <title>r</title>
   </set>
   <set>
      <color>yellow</color>
      <title>j</title>
      <title>w</title>
   </set>
</compound>

如果您想为cat所有 区别值产生类似的结果,那么它又是简短而简单的

<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:key name="kCatByVal" match="cat" use="."/>
 <xsl:key name="kColorByVal" match="color" use="."/>
 <xsl:key name="kTestByCat" match="test" use="cetval/cat"/>

 <xsl:variable name="vDistinctColors" select=
  "/*/*/set/color[generate-id()=generate-id(key('kColorByVal',.)[1])]"/>

 <xsl:template match="/">
  <xsl:apply-templates select=
   "/*/*/cetval/cat
        [generate-id()
        = generate-id(key('kCatByVal',.)[1])]"/>
 </xsl:template>

 <xsl:template match="cat">
  <xsl:variable name="vcurCat" select="."/>
  <compound>
    <cat><xsl:apply-templates/></cat>
    <xsl:for-each select="$vDistinctColors">
     <xsl:apply-templates select=
      "(key('kCatByVal',$vcurCat)/../../set/color[.=current()])[1]">
       <xsl:with-param name="pCat" select="$vcurCat"/>
      </xsl:apply-templates>
    </xsl:for-each>
  </compound>
 </xsl:template>

 <xsl:template match="color">
  <xsl:param name="pCat"/>

  <xsl:copy-of select="."/>
  <xsl:copy-of select="key('kTestByCat',$pCat)[set/color=current()]/title"/>
 </xsl:template>
</xsl:stylesheet>

当应用于同一个 XML 文档(上图)时,现在的结果是:

<compound>
   <cat>abc</cat>
   <color>blue</color>
   <title>k</title>
   <title>r</title>
   <color>yellow</color>
   <title>j</title>
   <title>w</title>
</compound>
<compound>
   <cat>fcg</cat>
   <color>blue</color>
   <title>k</title>
   <color>pink</color>
   <title>p</title>
   <color>yellow</color>
   <title>j</title>
</compound>
<compound>
   <cat>klm</cat>
   <color>pink</color>
   <title>p</title>
   <color>yellow</color>
   <title>j</title>
</compound>
<compound>
   <cat>dji</cat>
</compound>
<compound>
   <cat>kfjsdlk</cat>
</compound>
<compound>
   <cat>deiu</cat>
   <color>blue</color>
   <title>r</title>
</compound>
<compound>
   <cat>kfjdf</cat>
   <color>blue</color>
   <title>r</title>
</compound>

【讨论】:

以上是关于for-each 并使用 xslt 对 xml 进行排序和分组的主要内容,如果未能解决你的问题,请参考以下文章

XSLT for-each 无法提取值

从多个for-each XSLT段输出HTML

XSLT 1.0 / 使用条件对每个结果进行排序

在 msxsl 中将节点作为参数传递:来自 XSLT for-each 的脚本 javascript 函数不起作用

有没有办法在 XSLT 转换中用应用模板替换 for-each?

XSLT 2.0:根据 for-each -group 的当前索引评估和修改元素值