2011-01-11 3 views
0

Учитывая следующее Xml:Удаление повторяющихся узлов с выхода Xsl

<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 там дважды :(

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

С благодарностью

Dave

+0

Пожалуйста, , в отдельном вопросе объясните, какие требования/правила для производства продукции (в частности, неясно, как имена категорий должны быть сформированы). Я думаю, что может быть создано более простое решение, и проблему дублирования можно вообще избежать. –

+0

Ах да .. извините, номер представляет собой «уровень» категории, где «0» является корневым уровнем .. в этом случае «Спорт» и «1» - это первый уровень подкатегории, 2 - второй уровень подкатегории и т. д. Это обозначение, которое используется для отправки документа в Solr. Посмотрите на третий вариант в этом электронном письме http://search.lucidimagination.com/search/document/8a14673728e3a722/implementing_hierarchical_facet – CraftyFella

ответ

1

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

+0

Спасибо ..Я не был уверен, что это за узел. Просто посмотрел на него и, похоже, вы можете сделать это в .NET, используя xmlns: msxsl = "urn: schemas-microsoft-com: пространство имен xslt тоже :) – CraftyFella

+1

Если вы используете .NET и XslCompiledTransform, то вы можете использовать мою таблицу стилей как опубликованную (поскольку XslCompiledTransform поддерживает функцию набора узлов в пространстве имен EXSLT и Microsoft). Если вы используете .NET и XslTransform, тогда вы правы, вам нужно изменить пространство имен URI к тому, который вы опубликовали. –

+0

@CraftyFella: использовать 'XslCompiledTransform' лучше, потому что помимо других вещей он использует псевдостандартный URI пространства имен EXSLT. Также проверьте мой ответ для решения без расширения. –

1

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

<xsl:if test="not(/record/category 
       [. != ''] 
       [position() &lt; $pos] 
       [substring(., 1, string-length($field-text)) = $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

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

<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> 
Смежные вопросы