从 xsl 输出中删除重复节点

发布于 2024-10-11 13:44:51 字数 3736 浏览 3 评论 0原文

给定以下 Xml:

<record>
    <category>Sport/Racket Sports/Tennis</category>
    <category>Sport/Racket Sports/Badminton</category>
</record>

我正在尝试分解类别,以便生成以下 Xml:

<add>
   <doc>
      <field name="category_0">Sport</field>
      <field name="category_1">Sport/Racket Sports</field>
      <field name="category_2">Sport/Racket Sports/Tennis</field>
      <field name="category_2">Sport/Racket Sports/Badminton</field>
   </doc>
</add>

我已经设法生成了几乎就在那里的东西..我现在需要一种删除重复项的方法?有什么想法吗?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:template match="record">
        <add>
            <doc>
                <xsl:for-each select="category[. != '']">
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="''"/>
                        <xsl:with-param name="text" select="."/>
                        <xsl:with-param name="level" select="number(0)"/>
                    </xsl:call-template>
                </xsl:for-each>
            </doc>
        </add>
    </xsl:template>

    <xsl:template name="split-cats">
        <xsl:param name="text" select="."/>
        <xsl:param name="prefix"/>
        <xsl:param name="level" select="0"/>
            <xsl:choose>
                <xsl:when test="contains($text, '/')">
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, substring-before($text, '/'))"/>
                    </field>
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="concat($prefix, concat(substring-before($text, '/'), '/'))"/>
                        <xsl:with-param name="text" select="substring-after($text, '/')"/>
                        <xsl:with-param name="level" select="$level + 1"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, $text)"/>
                    </field>
                </xsl:otherwise>
            </xsl:choose>        
    </xsl:template>



</xsl:stylesheet>

此模板生成:

<add>
   <doc>
      <field name="category_0">Sport</field>
      <field name="category_1">Sport/Racket Sports</field>
      <field name="category_2">Sport/Racket Sports/Tennis</field>
      <field name="category_0">Sport</field>
      <field name="category_1">Sport/Racket Sports</field>
      <field name="category_2">Sport/Racket Sports/Badminton</field>
   </doc>
</add>

如您所见,其中有两次 SportSport/Racket Sports :(

仅供参考:我需要能够使用 XSLT 1.0 来完成此操作,

谢谢

Dave

Given the following Xml:

<record>
    <category>Sport/Racket Sports/Tennis</category>
    <category>Sport/Racket Sports/Badminton</category>
</record>

I'm trying to break up the categories so that the following Xml is produced:

<add>
   <doc>
      <field name="category_0">Sport</field>
      <field name="category_1">Sport/Racket Sports</field>
      <field name="category_2">Sport/Racket Sports/Tennis</field>
      <field name="category_2">Sport/Racket Sports/Badminton</field>
   </doc>
</add>

I've managed to produce something which is nearly there.. I now need a way of removing the duplicates? Any ideas?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:template match="record">
        <add>
            <doc>
                <xsl:for-each select="category[. != '']">
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="''"/>
                        <xsl:with-param name="text" select="."/>
                        <xsl:with-param name="level" select="number(0)"/>
                    </xsl:call-template>
                </xsl:for-each>
            </doc>
        </add>
    </xsl:template>

    <xsl:template name="split-cats">
        <xsl:param name="text" select="."/>
        <xsl:param name="prefix"/>
        <xsl:param name="level" select="0"/>
            <xsl:choose>
                <xsl:when test="contains($text, '/')">
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, substring-before($text, '/'))"/>
                    </field>
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="concat($prefix, concat(substring-before($text, '/'), '/'))"/>
                        <xsl:with-param name="text" select="substring-after($text, '/')"/>
                        <xsl:with-param name="level" select="$level + 1"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, $text)"/>
                    </field>
                </xsl:otherwise>
            </xsl:choose>        
    </xsl:template>



</xsl:stylesheet>

This template produces:

<add>
   <doc>
      <field name="category_0">Sport</field>
      <field name="category_1">Sport/Racket Sports</field>
      <field name="category_2">Sport/Racket Sports/Tennis</field>
      <field name="category_0">Sport</field>
      <field name="category_1">Sport/Racket Sports</field>
      <field name="category_2">Sport/Racket Sports/Badminton</field>
   </doc>
</add>

Which as you can see has Sport and Sport/Racket Sports in there twice :(

FYI: I need to be able to do this using XSLT 1.0

Thanks

Dave

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

背叛残局 2024-10-18 13:44:51

这是一种直接的方法,只需在代码创建的结果上使用 Muenchian 分组插入第二个转换步骤:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:exsl="http://exslt.org/common"
  exclude-result-prefixes="exsl">
    <xsl:output indent="yes"/>

    <xsl:key name="k1" match="cats/field" use="."/>

    <xsl:template match="record">
        <xsl:variable name="cats">
          <cats>
                <xsl:for-each select="category[. != '']">
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="''"/>
                        <xsl:with-param name="text" select="."/>
                        <xsl:with-param name="level" select="0"/>
                    </xsl:call-template>
                </xsl:for-each>
          </cats>
        </xsl:variable>
        <add>
            <doc>
              <xsl:copy-of select="exsl:node-set($cats)/cats/field[generate-id() = generate-id(key('k1', .)[1])]"/>
            </doc>
        </add>
    </xsl:template>

    <xsl:template name="split-cats">
        <xsl:param name="text" select="."/>
        <xsl:param name="prefix"/>
        <xsl:param name="level" select="0"/>
            <xsl:choose>
                <xsl:when test="contains($text, '/')">
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, substring-before($text, '/'))"/>
                    </field>
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="concat($prefix, concat(substring-before($text, '/'), '/'))"/>
                        <xsl:with-param name="text" select="substring-after($text, '/')"/>
                        <xsl:with-param name="level" select="$level + 1"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, $text)"/>
                    </field>
                </xsl:otherwise>
            </xsl:choose>        
    </xsl:template>



</xsl:stylesheet>

这会使用 exsl:node-set 但是,如果您在浏览器中以 XSLT 1.0 为目标那么对于 IE/MSXML,您需要使用 中显示的脚本修复它http://dpcarlisle.blogspot.com/2007/05/exslt-node-set-function.html

Here is a straight-forward approach that simply inserts a second transformation step using Muenchian grouping on the result your code creates:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:exsl="http://exslt.org/common"
  exclude-result-prefixes="exsl">
    <xsl:output indent="yes"/>

    <xsl:key name="k1" match="cats/field" use="."/>

    <xsl:template match="record">
        <xsl:variable name="cats">
          <cats>
                <xsl:for-each select="category[. != '']">
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="''"/>
                        <xsl:with-param name="text" select="."/>
                        <xsl:with-param name="level" select="0"/>
                    </xsl:call-template>
                </xsl:for-each>
          </cats>
        </xsl:variable>
        <add>
            <doc>
              <xsl:copy-of select="exsl:node-set($cats)/cats/field[generate-id() = generate-id(key('k1', .)[1])]"/>
            </doc>
        </add>
    </xsl:template>

    <xsl:template name="split-cats">
        <xsl:param name="text" select="."/>
        <xsl:param name="prefix"/>
        <xsl:param name="level" select="0"/>
            <xsl:choose>
                <xsl:when test="contains($text, '/')">
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, substring-before($text, '/'))"/>
                    </field>
                    <xsl:call-template name="split-cats">
                        <xsl:with-param name="prefix" select="concat($prefix, concat(substring-before($text, '/'), '/'))"/>
                        <xsl:with-param name="text" select="substring-after($text, '/')"/>
                        <xsl:with-param name="level" select="$level + 1"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <field>
                        <xsl:attribute name="name">
                            <xsl:text>category_</xsl:text><xsl:value-of select="$level"/>
                        </xsl:attribute>
                        <xsl:value-of select="concat($prefix, $text)"/>
                    </field>
                </xsl:otherwise>
            </xsl:choose>        
    </xsl:template>



</xsl:stylesheet>

That makes use of exsl:node-set however, if you target XSLT 1.0 in the browsers then for IE/MSXML you need to fix it with the script shown in http://dpcarlisle.blogspot.com/2007/05/exslt-node-set-function.html.

何其悲哀 2024-10-18 13:44:51

另一种不使用扩展函数(但不一定像使用 Muenchian 分组那样有效)的方法是添加一个检查来比较以前的类别记录,以查看它们是否以您要处理的字符串开头

<xsl:if test="not(/record/category
              [. != '']
              [position() < $pos]
              [substring(., 1, string-length($field-text)) = $field-text])">

。代码中的$pos是一个参数,包含当前匹配的category元素的位置,$field-text是一个变量,包含您要输出的文本。

这是完整的 XSLT 样式表,它也应该为您提供所需的输出

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

   <xsl:template match="record">
      <add>
         <doc>
            <xsl:for-each select="category[. != '']">
               <xsl:call-template name="split-cats">
                  <xsl:with-param name="prefix" select="''"/>
                  <xsl:with-param name="text" select="."/>
                  <xsl:with-param name="level" select="number(0)"/>
                  <!-- Position of the current category -->
                  <xsl:with-param name="pos" select="position()"/>
               </xsl:call-template>
            </xsl:for-each>
         </doc>
      </add>
   </xsl:template>

   <xsl:template name="split-cats">
      <xsl:param name="text" select="."/>
      <xsl:param name="prefix"/>
      <xsl:param name="level" select="0"/>
      <xsl:param name="pos"/>
      <xsl:choose>
         <xsl:when test="contains($text, '/')">
            <xsl:variable name="field-text" select="concat($prefix, substring-before($text, '/'))"/>
            <!-- Test no previous category begins with the text we are about to output -->
            <xsl:if test="not(/record/category
                             [. != '']
                             [position() < $pos]
                             [substring(., 1, string-length($field-text)) = $field-text])">
               <field>
                  <xsl:attribute name="name">
                     <xsl:text>category_</xsl:text>
                     <xsl:value-of select="$level"/>
                  </xsl:attribute>
                  <xsl:value-of select="$field-text"/>
               </field>
            </xsl:if>
            <xsl:call-template name="split-cats">
               <xsl:with-param name="prefix" select="concat($prefix, concat(substring-before($text, '/'), '/'))"/>
               <xsl:with-param name="text" select="substring-after($text, '/')"/>
               <xsl:with-param name="level" select="$level + 1"/>
               <xsl:with-param name="pos" select="$pos"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:variable name="field-text" select="concat($prefix, $text)"/>
            <!-- Test no previous category begins with the text we are about to output -->
            <xsl:if test="not(/record/category
                             [. != '']
                             [position() < $pos]
                             [substring(., 1, string-length($field-text)) = $field-text])">
               <field>
                  <xsl:attribute name="name">
                     <xsl:text>category_</xsl:text>
                     <xsl:value-of select="$level"/>
                  </xsl:attribute>
                  <xsl:value-of select="$field-text"/>
               </field>
            </xsl:if>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

Another way to do this without making use of extension function (but which will not necessarily be as efficient asusing Muenchian grouping) is to add a check to compare previous category records to see if they begin with the string you are about to about

<xsl:if test="not(/record/category
              [. != '']
              [position() < $pos]
              [substring(., 1, string-length($field-text)) = $field-text])">

In this bit of code $pos is a parameter containing the position of the current category element you are currently matching, and $field-text is a variable containing the text you are about to output.

Here is the complete XSLT stylesheet, which should also give you the output you desire

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

   <xsl:template match="record">
      <add>
         <doc>
            <xsl:for-each select="category[. != '']">
               <xsl:call-template name="split-cats">
                  <xsl:with-param name="prefix" select="''"/>
                  <xsl:with-param name="text" select="."/>
                  <xsl:with-param name="level" select="number(0)"/>
                  <!-- Position of the current category -->
                  <xsl:with-param name="pos" select="position()"/>
               </xsl:call-template>
            </xsl:for-each>
         </doc>
      </add>
   </xsl:template>

   <xsl:template name="split-cats">
      <xsl:param name="text" select="."/>
      <xsl:param name="prefix"/>
      <xsl:param name="level" select="0"/>
      <xsl:param name="pos"/>
      <xsl:choose>
         <xsl:when test="contains($text, '/')">
            <xsl:variable name="field-text" select="concat($prefix, substring-before($text, '/'))"/>
            <!-- Test no previous category begins with the text we are about to output -->
            <xsl:if test="not(/record/category
                             [. != '']
                             [position() < $pos]
                             [substring(., 1, string-length($field-text)) = $field-text])">
               <field>
                  <xsl:attribute name="name">
                     <xsl:text>category_</xsl:text>
                     <xsl:value-of select="$level"/>
                  </xsl:attribute>
                  <xsl:value-of select="$field-text"/>
               </field>
            </xsl:if>
            <xsl:call-template name="split-cats">
               <xsl:with-param name="prefix" select="concat($prefix, concat(substring-before($text, '/'), '/'))"/>
               <xsl:with-param name="text" select="substring-after($text, '/')"/>
               <xsl:with-param name="level" select="$level + 1"/>
               <xsl:with-param name="pos" select="$pos"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:variable name="field-text" select="concat($prefix, $text)"/>
            <!-- Test no previous category begins with the text we are about to output -->
            <xsl:if test="not(/record/category
                             [. != '']
                             [position() < $pos]
                             [substring(., 1, string-length($field-text)) = $field-text])">
               <field>
                  <xsl:attribute name="name">
                     <xsl:text>category_</xsl:text>
                     <xsl:value-of select="$level"/>
                  </xsl:attribute>
                  <xsl:value-of select="$field-text"/>
               </field>
            </xsl:if>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>
递刀给你 2024-10-18 13:44:51

如果没有扩展函数,此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="record">
        <add>
            <doc>
                <xsl:apply-templates select="category[1]"/>
            </doc>
        </add>
    </xsl:template>
    <xsl:template match="category" name="category">
        <xsl:param name="pOutput"/>
        <xsl:param name="pPrefix"/>
        <xsl:param name="pLevel" select="0"/>
        <xsl:param name="pSequence" select="concat(.,'/')"/>
        <xsl:choose>
            <xsl:when test="$pSequence">
                <xsl:variable name="vItem"
                              select="concat($pPrefix,
                                             substring-before($pSequence,
                                                              '/'))"/>
                <xsl:variable name="vOutput"
                              select="concat('|',$vItem,'|')"/>
                <xsl:if test="not(contains($pOutput,$vOutput))">
                    <field name="category_{$pLevel}">
                        <xsl:value-of select="$vItem"/>
                    </field>
                </xsl:if>
                <xsl:call-template name="category">
                    <xsl:with-param name="pOutput"
                                    select="concat($pOutput,$vOutput)"/>
                    <xsl:with-param name="pPrefix" 
                                    select="concat($vItem,'/')"/>
                    <xsl:with-param name="pLevel" select="$pLevel + 1"/>
                    <xsl:with-param name="pSequence"
                                    select="substring-after($pSequence,'/')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="following-sibling::category[1]">
                    <xsl:with-param name="pOutput" select="$pOutput"/>
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

输出:

<add>
    <doc>
        <field name="category_0">Sport</field>
        <field name="category_1">Sport/Racket Sports</field>
        <field name="category_2">Sport/Racket Sports/Tennis</field>
        <field name="category_2">Sport/Racket Sports/Badminton</field>
    </doc>
</add>

Without extension functions, this stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="record">
        <add>
            <doc>
                <xsl:apply-templates select="category[1]"/>
            </doc>
        </add>
    </xsl:template>
    <xsl:template match="category" name="category">
        <xsl:param name="pOutput"/>
        <xsl:param name="pPrefix"/>
        <xsl:param name="pLevel" select="0"/>
        <xsl:param name="pSequence" select="concat(.,'/')"/>
        <xsl:choose>
            <xsl:when test="$pSequence">
                <xsl:variable name="vItem"
                              select="concat($pPrefix,
                                             substring-before($pSequence,
                                                              '/'))"/>
                <xsl:variable name="vOutput"
                              select="concat('|',$vItem,'|')"/>
                <xsl:if test="not(contains($pOutput,$vOutput))">
                    <field name="category_{$pLevel}">
                        <xsl:value-of select="$vItem"/>
                    </field>
                </xsl:if>
                <xsl:call-template name="category">
                    <xsl:with-param name="pOutput"
                                    select="concat($pOutput,$vOutput)"/>
                    <xsl:with-param name="pPrefix" 
                                    select="concat($vItem,'/')"/>
                    <xsl:with-param name="pLevel" select="$pLevel + 1"/>
                    <xsl:with-param name="pSequence"
                                    select="substring-after($pSequence,'/')"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="following-sibling::category[1]">
                    <xsl:with-param name="pOutput" select="$pOutput"/>
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Output:

<add>
    <doc>
        <field name="category_0">Sport</field>
        <field name="category_1">Sport/Racket Sports</field>
        <field name="category_2">Sport/Racket Sports/Tennis</field>
        <field name="category_2">Sport/Racket Sports/Badminton</field>
    </doc>
</add>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文