0

我的提要中的每个产品都有这样的类别节点:

<categories>
  <category>För henne &gt; Ansikte</category>
  <category>För henne &gt; Pumps &amp; klackskor</category>
  <category>För henne &gt; Stövlar &amp; stövletter</category>
  <category>För honom &gt; Badkläder</category>
  ...
</categories>

哪里&gt;将父类与子类分开。

我想要做的是有这样的类别树:

<categories>
  <category name="För henne">
    <categories>
      <category name="Ansikte"/>
      <category name="Pumps &amp; klackskor"/>
      <category name="Stövlar &amp; stövletter"/>
    </categories>
  </category>
  <category name="För honom">
    <categories>
      <category name="Badkläder"/>
    </categories>
  </category>
</categories>

我怎样才能用 xsl 实现这一点?

4

2 回答 2

1

这是另一种 XSLT 2.0 样式表,我认为它是最简单但通用的解决方案。

这种转变...

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="sep" select="' &gt; '" />

<xsl:template match="categories" name="cat-grouper">
  <xsl:param name="cat-strs" as="xs:string*" select="category" />
  <categories>
    <xsl:for-each-group select="$cat-strs[contains(.,$sep)]" group-by="substring-before(.,$sep)">
      <category name="{current-grouping-key()}">
        <xsl:call-template name="cat-grouper">
          <xsl:with-param name="cat-strs" select="for $s in current-group() return substring-after($s,$sep)" />
        </xsl:call-template>
      </category>
    </xsl:for-each-group>
    <xsl:for-each-group select="$cat-strs[not(contains(.,$sep))]" group-by=".">
      <category name="{current-grouping-key()}" />
    </xsl:for-each-group>
  </categories> 
</xsl:template>

</xsl:stylesheet>

...应用于以下输入(改编自 OP 的示例输入,以解决他与输入中两次出现有关的评论<category>För honom > Badkläder</category>...

<categories>
  <category>För henne &gt; Ansikte</category>
  <category>För henne &gt; Pumps &amp; klackskor</category>
  <category>För henne &gt; Stövlar &amp; stövletter</category>
  <category>För honom &gt; Badkläder</category>
  <category>För honom &gt; Badkläder</category>
</categories>

...产量...

<categories>
   <category name="För henne">
      <categories>
         <category name="Ansikte"/>
         <category name="Pumps &amp; klackskor"/>
         <category name="Stövlar &amp; stövletter"/>
      </categories>
   </category>
   <category name="För honom">
      <categories>
         <category name="Badkläder"/>
      </categories>
   </category>
</categories>

笔记

通过双重使用唯一的模板可以获得一些简单性。它既是categories根元素的匹配模板,也是处理类别字符串列表的命名模板。

另一个

这是另一种替代解决方案。如果更喜欢我提供给你的第一个,但值得看看这个。使用 tokenize() 的类似用法,您可能会认为这只是对 Dimitre 解决方案的调整。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="sep" select="' &gt; '" />

<xsl:template match="categories" name="cat-grouper">
  <xsl:param name="cat-strs" as="xs:string*" select="category" />
  <categories>
    <xsl:for-each-group select="$cat-strs" group-by="tokenize(.,$sep)[1]">
      <category name="{current-grouping-key()}">
        <xsl:variable name="subcats" select="
          for $s in current-group()[contains(.,$sep)]
            return substring-after($s,$sep)" />
        <xsl:if test="exists($subcats)">
          <xsl:call-template name="cat-grouper">
            <xsl:with-param name="cat-strs" select="$subcats" />
          </xsl:call-template>
        </xsl:if>  
      </category>
    </xsl:for-each-group>
  </categories> 
</xsl:template>

</xsl:stylesheet>

附录

这是将我的第一个解决方案应用于 Dimitre 的输入文档的结果输出。我要求 OP 建议这是否是对他的转换规则的正确解释。这是我的理解,但也许我理解错了?

<categories>
   <category name="A">
      <categories>
         <category name="AB1">
            <categories>
               <category name="AB1C1"/>
            </categories>
         </category>
         <category name="AB2">
            <categories>
               <category name="AB2C1">
                  <categories>
                     <category name="AB2C1D1"/>
                     <category name="AB2C3"/>
                  </categories>
               </category>
               <category name="AB2C1"/>
            </categories>
         </category>
      </categories>
   </category>
   <category name="F">
      <categories>
         <category name="XY1">
            <categories>
               <category name="XY1Z1"/>
            </categories>
         </category>
         <category name="XY2">
            <categories>
               <category name="XY2Z1">
                  <categories>
                     <category name="XY2Z1T1"/>
                     <category name="XY2Z3"/>
                  </categories>
               </category>
               <category name="XY2Z1"/>
            </categories>
         </category>
      </categories>
   </category>
</categories>
于 2012-10-14T11:18:42.890 回答
0

一、XSLT 1.0 解决方案

这种转变

<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="kCategory" match="category"
  use="substring-before(., ' >')"/>

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

 <xsl:template match=
  "category
    [generate-id()
    =
     generate-id(key('kCategory', substring-before(., ' >'))[1])
    ]">
  <category name="{substring-before(., ' >')}">
    <categories>
      <xsl:apply-templates mode="inGroup"
        select="key('kCategory', substring-before(., ' >'))"/>
    </categories>
  </category>
 </xsl:template>

 <xsl:template match="category" mode="inGroup">
   <category name="{substring-after(., '> ')}"/>
 </xsl:template>
 <xsl:template match="category"/>
</xsl:stylesheet>

应用于提供的 XML 文档时:

<categories>
  <category>För henne > Ansikte</category>
  <category>För henne > Pumps &amp; klackskor</category>
  <category>För henne > Stövlar &amp; stövletter</category>
  <category>För honom > Badkläder</category>
  ...
</categories>

产生想要的正确结果:

<categories>
   <category name="För henne">
      <categories>
         <category name="Ansikte"/>
         <category name="Pumps &amp; klackskor"/>
         <category name="Stövlar &amp; stövletter"/>
      </categories>
   </category>
   <category name="För honom">
      <categories>
         <category name="Badkläder"/>
      </categories>
   </category>
  ...
</categories>

说明

  1. 正确使用 Muenchian 分组方法。

  2. 正确使用和覆盖身份规则。

  3. 正确使用按键和模式。


二、通用 XSLT 2.0 解决方案

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my" exclude-result-prefixes="my xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <xsl:sequence select="my:grouping(*)"/>
 </xsl:template>

 <xsl:function name="my:grouping">
  <xsl:param name="pStrings" as="xs:string*"/>

  <xsl:if test="$pStrings[1]">
      <categories>
          <xsl:for-each-group select="$pStrings"
            group-by="tokenize(., ' > ')[1]">

            <category name="{current-grouping-key()}">
             <xsl:variable name="vnewStrings" as="xs:string*">
                 <xsl:for-each select="current-group()">
                   <xsl:sequence select="substring-after(., ' > ')"/>
                 </xsl:for-each>
             </xsl:variable>
             <xsl:sequence select="my:grouping($vnewStrings)"/>
            </category>
          </xsl:for-each-group>
      </categories>
  </xsl:if>
 </xsl:function>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时:

<categories>
  <category>A > AB1 > AB1C1</category>
  <category>A > AB2 > AB2C1 > AB2C1D1</category>
  <category>A > AB2 > AB2C1</category>
  <category>A > AB2 > AB2C1 > AB2C3</category>
  <category>F > XY1 > XY1Z1</category>
  <category>F > XY2 > XY2Z1 > XY2Z1T1</category>
  <category>F > XY2 > XY2Z1</category>
  <category>F > XY2 > XY2Z1 > XY2Z3</category>
</categories>

产生了想要的正确结果:

<categories>
   <category name="A">
      <categories>
         <category name="AB1">
            <categories>
               <category name="AB1C1"/>
            </categories>
         </category>
         <category name="AB2">
            <categories>
               <category name="AB2C1">
                  <categories>
                     <category name="AB2C1D1"/>
                     <category name="AB2C3"/>
                  </categories>
               </category>
            </categories>
         </category>
      </categories>
   </category>
   <category name="F">
      <categories>
         <category name="XY1">
            <categories>
               <category name="XY1Z1"/>
            </categories>
         </category>
         <category name="XY2">
            <categories>
               <category name="XY2Z1">
                  <categories>
                     <category name="XY2Z1T1"/>
                     <category name="XY2Z3"/>
                  </categories>
               </category>
            </categories>
         </category>
      </categories>
   </category>
</categories>

说明

  1. 用于<xsl:for-each-group>

  2. 使用标准 XSLT 函数:current-group()current-grouping-key().

  3. 带停止条件的递归:空参数。

于 2012-10-13T21:22:05.037 回答