Удаление дубликатов узлов из вывода xsl - PullRequest
0 голосов
/ 11 января 2011

С учетом следующего 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>

Который, как вы можете видеть, имеет Sport и Sport/Racket Sports там дважды: (

К вашему сведению: Мне нужно быть в состоянии сделать это, используя XSLT 1.0

Спасибо

Дейв

Ответы [ 3 ]

1 голос
/ 11 января 2011

Другой способ сделать это без использования функции расширения (но которая не обязательно будет столь же эффективной, как использование мюнхенской группировки) - это добавить проверку, чтобы сравнить предыдущие записи категории, чтобы увидеть, начинаются ли они со строки, которую вы собираетесь примерно

<xsl:if test="not(/record/category
              [. != '']
              [position() &lt; $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() &lt; $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() &lt; $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>
1 голос
/ 12 января 2011

Без функций расширения эта таблица стилей:

<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>
1 голос
/ 11 января 2011

Вот простой подход, который просто вставляет второй шаг преобразования, используя группировку 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.

...