2011-01-06 4 views
1

Как я могу удалить повторяющиеся узлы на основе значений нескольких (более 1) атрибутов? Также имена атрибутов передаются как параметры в таблицу стилей. Теперь я знаю о методе группировки Muenchian, который использует элемент <xsl:key>. Но я узнал, что XSLT 1.0 не позволяет параметрам/переменным в <xsl:key>.Как устранить дублирующие базы узлов на значениях нескольких атрибутов?

Есть ли другой способ (ы) для удаления дубликатов узлов? Это нормально, если это не так эффективно, как метод Мунечяна.

Обновление от previus question:

XML:

<data id = "root"> 
    <record id="1" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="2" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="3" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="4" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="5" operator1='xxx' operator2='lkj' operator3='tyu'/> 
    <record id="6" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="7" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="8" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="9" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="10" operator1='rrr' operator2='yyy' operator3='zzz'/> 
</data> 
+0

Вы можете добавить пример xml и необходимый результат на свой вопрос – Treemonkey

ответ

2

Используйте это преобразование (просто и нет необходимости для создания новой таблицы стилей):

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:param name="pAttribs"> 
<name>operator1</name> 
<name>operator2</name> 
<name>operator3</name> 
</xsl:param> 

<xsl:variable name="vAttribs" select= 
    "document('')/*/xsl:param[@name='pAttribs']"/> 

<xsl:key name="kRecByAtts" match="record" 
    use="@___g_key"/> 

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

<xsl:template match="/"> 
<xsl:variable name="vrtdPass1"> 
    <xsl:apply-templates/> 
</xsl:variable> 

<xsl:variable name="vPass1" select= 
    "ext:node-set($vrtdPass1)/*"/> 

<xsl:apply-templates select="$vPass1"/> 
</xsl:template> 

<xsl:template match="record[not(@___g_key)]"> 
<xsl:copy> 
    <xsl:copy-of select="@*"/> 

    <xsl:attribute name="___g_key"> 
    <xsl:for-each select="@*[name()=$vAttribs/name]"> 
     <xsl:sort select="name()"/> 

     <xsl:value-of select= 
      "concat('___Attrib___',name(),'___Value___',.,'+++')"/> 
    </xsl:for-each> 
    </xsl:attribute> 
</xsl:copy> 
</xsl:template> 

<xsl:template match= 
    "record[@___g_key] 
     [not(generate-id() 
      = 
       generate-id(key('kRecByAtts', @___g_key)[1]) 
      ) 
      ] 
    "/> 

    <xsl:template match="@___g_key"/> 
</xsl:stylesheet> 

Применительно к XML-документа вашего предыдущего вопроса:

<data id = "root"> 
    <record id="1" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="2" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="3" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="4" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="5" operator1='xxx' operator2='lkj' operator3='tyu'/> 
    <record id="6" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="7" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="8" operator1='abc' operator2='yyy' operator3='zzz'/> 
    <record id="9" operator1='xxx' operator2='yyy' operator3='zzz'/> 
    <record id="10" operator1='rrr' operator2='yyy' operator3='zzz'/> 
</data> 

требуемый, правильный результат:

<data id="root"> 
    <record id="1" operator1="xxx" operator2="yyy" operator3="zzz"/> 
    <record id="2" operator1="abc" operator2="yyy" operator3="zzz"/> 
    <record id="5" operator1="xxx" operator2="lkj" operator3="tyu"/> 
    <record id="10" operator1="rrr" operator2="yyy" operator3="zzz"/> 
</data> 
+0

+1. Здесь осталось много оборотов. – Flack

+0

@Dimitre: Единственная проблема, с которой я столкнулась с этим способом генерации значения нового атрибута, - это возможность отсутствия исходных атрибутов: столкновение ключей может произойти для 'record' без' @ operator2' и 'record' без' @ operator3' , –

+0

@Alejandro: Спасибо за другое хорошее наблюдение. Это немедленное исправление, и я сделаю это иногда позже этим днем, когда у меня будет 10 минут свободного времени. –

1

Если вы хотите передать имена атрибутов в качестве параметра, то один подход может быть два шага преобразования, где первый шаг принимает любой ввод XML и просто имена атрибутов и имена элементов в качестве параметров для создания второй таблицы стилей, которая затем устраняет дубликаты. Вот пример первой таблицы стилей:

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:exsl="http://exslt.org/common" 
    xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" 
    exclude-result-prefixes="axsl exsl" 
    version="1.0"> 

    <xsl:param name="parent-name" select="'items'"/> 
    <xsl:param name="element-name" select="'item'"/> 
    <xsl:param name="att-names" select="'att1,att2'"/> 
    <xsl:param name="sep" select="'|'"/> 

    <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/> 

    <xsl:output method="xml" indent="yes"/> 

    <xsl:variable name="key-value"> 
    <xsl:text>concat(</xsl:text> 
    <xsl:call-template name="define-values"> 
     <xsl:with-param name="att-names" select="$att-names"/> 
    </xsl:call-template> 
    <xsl:text>)</xsl:text> 
    </xsl:variable> 

    <xsl:template name="define-values"> 
    <xsl:param name="att-names"/> 
    <xsl:choose> 
     <xsl:when test="contains($att-names, ',')"> 
     <xsl:value-of select="concat('@', substring-before($att-names, ','), ',&quot;', $sep, '&quot;,')"/> 
     <xsl:call-template name="define-values"> 
      <xsl:with-param name="att-names" select="substring-after($att-names, ',')"/> 
     </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:value-of select="concat('@', $att-names)"/> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:template> 

    <xsl:template match="/"> 
    <axsl:stylesheet version="1.0"> 
     <axsl:output indent="yes"/> 
     <axsl:key name="k1" match="{$parent-name}/{$element-name}" use="{$key-value}"/> 
     <axsl:template match="@* | node()"> 
     <axsl:copy> 
      <axsl:apply-templates select="@* | node()"/> 
     </axsl:copy> 
     </axsl:template> 
     <axsl:template match="{$parent-name}"> 
     <axsl:copy> 
      <axsl:apply-templates select="@*"/> 
      <axsl:apply-templates select="{$element-name}[generate-id() = generate-id(key('k1', {$key-value})[1])]"/> 
     </axsl:copy> 
     </axsl:template> 
    </axsl:stylesheet> 
    </xsl:template> 

</xsl:stylesheet> 

Он принимает четыре параметра:

  1. : имя элемента, содержащего эти элементы которого вы хотите исключить дубликаты
  2. element-name: имя из тех элементов, из которых вы хотите удалить дубликаты
  3. att-names: список имен атрибутов, разделенных запятыми
  4. sep: разделитель символов, которые не должны возникать в значениях атрибутов во входном XML

Таблица стилей затем генерирует вторую таблицу стилей, которая применяется Muenchian группировку для устранения дубликатов. Например, с параметрами по умолчанию, указанными в таблице стилей Saxon 6.5.5 генерирует следующий таблицу стилей:

<axsl:stylesheet xmlns:axsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <axsl:output indent="yes"/> 
    <axsl:key name="k1" match="items/item" use="concat(@att1,&#34;|&#34;,@att2)"/> 
    <axsl:template match="@* | node()"> 
     <axsl:copy> 
     <axsl:apply-templates select="@* | node()"/> 
     </axsl:copy> 
    </axsl:template> 
    <axsl:template match="items"> 
     <axsl:copy> 
     <axsl:apply-templates select="@*"/> 
     <axsl:apply-templates select="item[generate-id() = generate-id(key('k1', concat(@att1,&#34;|&#34;,@att2))[1])]"/> 
     </axsl:copy> 
    </axsl:template> 
</axsl:stylesheet> 

Это может быть применен к документу XML, как

<items> 
    <item att1="a" att2="1" att3="A"/> 
    <item att1="b" att2="1" att3="A"/> 
    <item att1="a" att2="1" att3="B"/> 
    <item att1="c" att2="2" att3="A"/> 
    <item att1="d" att2="3" att3="C"/> 
</items> 

, а выход

<items> 
    <item att1="a" att2="1" att3="A"/> 
    <item att1="b" att2="1" att3="A"/> 
    <item att1="c" att2="2" att3="A"/> 
    <item att1="d" att2="3" att3="C"/> 
</items> 
+0

+1 Для стилей в качестве вывода. –

+0

+1. Отмеченный. – Flack

+0

Я также хочу, чтобы 3-й узел из входного xml был удален в выпуске. Если вы видите, что 3-й узел не должен быть удален, так как он является уникальным узлом (ни один другой узел на входе точно такой же, как этот узел, на основе всех 3 атрибутов). Дайте мне знать, если этот клик. – JayRaj

3

Другой подход для одной трансформации в два этапа :

<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:key name="kItemByLocal" match="record[@local-key]" use="@local-key"/> 
    <xsl:param name="pAttNames" select="'operator1 operator2 operator3'"/> 
    <xsl:template match="/"> 
     <xsl:variable name="vFirstRTF"> 
      <xsl:apply-templates/> 
     </xsl:variable> 
     <xsl:apply-templates select="msxsl:node-set($vFirstRTF)/node()"/> 
    </xsl:template> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="record[not(@local-key)]"> 
     <xsl:copy> 
      <xsl:attribute name="local-key"> 
       <xsl:call-template name="local-key"/> 
      </xsl:attribute> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="record[@local-key] 
           [count(.|key('kItemByLocal',@local-key)[1]) 
           != 1]|@local-key"/> 
    <xsl:template name="local-key"> 
     <xsl:param name="pAttributes" select="concat($pAttNames,' ')"/> 
     <xsl:if test="normalize-space($pAttributes)"> 
      <xsl:variable name="vName" 
          select="substring-before($pAttributes,' ')"/> 
      <xsl:variable name="vAttribute" select="@*[name()=$vName]"/> 
      <xsl:value-of select="concat($vName,'+',$vAttribute,'+')"/> 
      <xsl:call-template name="local-key"> 
       <xsl:with-param name="pAttributes" 
           select="substring-after($pAttributes,' ')"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Выход:

<data id="root"> 
    <record id="1" operator1="xxx" operator2="yyy" operator3="zzz"></record> 
    <record id="2" operator1="abc" operator2="yyy" operator3="zzz"></record> 
    <record id="5" operator1="xxx" operator2="lkj" operator3="tyu"></record> 
    <record id="10" operator1="rrr" operator2="yyy" operator3="zzz"></record> 
</data> 

Edit: Также без имени шаблона для @local-key поколения

<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:key name="kItemByLocal" match="record[@local-key]" use="@local-key"/> 
    <xsl:param name="pAttNames" select="'operator1 operator2 operator3'"/> 
    <xsl:template match="/"> 
     <xsl:variable name="vFirstRTF"> 
      <xsl:apply-templates/> 
     </xsl:variable> 
     <xsl:apply-templates select="msxsl:node-set($vFirstRTF)/node()"/> 
    </xsl:template> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="record[not(@local-key)]"> 
     <xsl:variable name="vAttNames" 
         select="concat(' ',$pAttNames,' ')"/> 
     <xsl:copy> 
      <xsl:attribute name="local-key"> 
       <xsl:for-each select="@*[contains(
              $vAttNames, 
              concat(' ',name(),' ') 
               )]"> 
        <xsl:sort select="substring-before(
              $vAttNames, 
              concat(' ',name(),' ') 
                )"/> 
        <xsl:value-of select="concat(name(),'++',.,'++')"/> 
       </xsl:for-each> 
      </xsl:attribute> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="record[@local-key] 
           [count(.|key('kItemByLocal',@local-key)[1]) 
           != 1]|@local-key"/> 
</xsl:stylesheet> 

Примечание: Если вы уверены в том, что атрибуты порядок является одинаковым для всех элементов, то может удалить сортировку.

+0

+1 также хорошее решение. – Flack

+0

+1 для того, чтобы быть более осторожным, чем я, и не следить за очевидными вещами. –

+0

@ Alejandro: Спасибо большое! – JayRaj