2012-04-04 1 views
3

Я пытаюсь получить список значений атрибутов для дочерних элементов элемента, но хочу, чтобы значения отображались только один раз.Выберите disctinct в XSLT относительно каждого элемента

Например, у меня есть следующий XML

<root> 
    <sec> 
     <nom-title> 
      <nom-chapter> 
       <nom-article><data att="1.1"/></nom-article> 
       <nom-article> 
        <nom-item><data att="1.1"/></nom-item> 
        <nom-item><data att="1.2"/></nom-item> 
       </nom-article> 
      </nom-chapter> 
      <nom-chapter> 
       <nom-article><data att="2.1"/></nom-article> 
       <nom-article><data att="1.1"/></nom-article> 
      </nom-chapter> 
     </nom-title> 
     <nom-title> 
      <nom-chapter> 
       <nom-article><data att="1.1"/></nom-article> 
      </nom-chapter> 
     </nom-title> 
    </sec> 
</root> 

И я хочу, результат подобного:

<root> 
    <nom-title> 
     <att>1.1</att> 
     <att>1.2</att> 
     <att>2.1</att> 
     <nom-chapter> 
      <att>1.1</att> 
      <att>1.2</att> 
      <nom-article> 
       <att>1.1</att> 
      </nom-article> 
      <nom-article> 
       <att>1.1</att> 
       <att>1.2</att> 
       <nom-item><att>1.1</att></nom-item> 
       <nom-item><att>1.2</att></nom-item> 
      </nom-article> 
     </nom-chapter> 
    </nom-title> 
    <nom-title> 
     <att>1.1</att> 
     <nom-chapter> 
      <att>1.1</att> 
      <nom-article> 
       <att>1.1</att> 
      </nom-article> 
     </nom-chapter> 
    </nom-title> 
</root> 

Я пытался использовать XSL: ключевой элемент, но он возвращает только значение для одного элемента. В этом примере он возвращает только 1.1 для первого заголовка, но не второго. XSL я использовал:

<xsl:key name="allAtt" 
    match="//*[starts-with(name(.),'nom-')]/data" 
    use="@att"/> 
<xsl:template match="nom-title|nom-chapter|nom-article|nom-item"> 
    <xsl:element name="name(.)"> 
     <xsl:apply-templates select=".//*[starts-with(name(.),'nom-')]/data 
    </xsl:element> 
</xsl:template>   
<xsl:template match="data"> 
     <xsl:variable name="att" select="@att"/> 
     <xsl:if test="generate-id(.)=generate-id(key('allAtt',$att)[1]"> 
      <xsl:element name="att"><xsl:value-of select="$att"></xsl:element> 
     </xsl:if> 
</xsl:template> 
+2

В моем опыте с xslt для начала нужно иметь действительный xml. Похоже, элемент «nom-article» не закрыт. Возможно, сначала захочется исправить это. –

+1

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

+0

Извините, неправильная копия, я исправил ее. – claudex

ответ

2

Это преобразование:

<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="kData-nom-article" match="data" use= 
"concat(generate-id(ancestor::nom-article[1]), 
     '+', @att)"/> 
<xsl:key name="kData-nom-chapter" match="data" use= 
"concat(generate-id(ancestor::nom-chapter[1]), 
     '+', @att)"/> 
<xsl:key name="kData-nom-title" match="data" use= 
"concat(generate-id(ancestor::nom-title[1]), 
     '+', @att)"/> 

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

<xsl:template match="sec"><xsl:apply-templates/></xsl:template> 

<xsl:template match="nom-title|nom-article|nom-chapter"> 
    <xsl:copy> 
    <xsl:apply-templates mode="list" select= 
    ".//data[generate-id() 
      = 
       generate-id(key(concat('kData-', name(current())), 
           concat(generate-id(current()), 
            '+', @att 
            ) 
          ) 
           [1] 
         ) 
      ]"/> 
    <xsl:apply-templates/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="data" mode="list"> 
    <att><xsl:value-of select="@att"/></att> 
</xsl:template> 

<xsl:template match="non-item/data"> 
    <att><xsl:value-of select="@att"/></att> 
</xsl:template> 

<xsl:template match="*[not(self::nom-item)]/data"/> 
</xsl:stylesheet> 

при нанесении на поставленном XML документа:

<root> 
    <sec> 
     <nom-title> 
      <nom-chapter> 
       <nom-article> 
        <data att="1.1"/> 
       </nom-article> 
       <nom-article> 
        <nom-item> 
         <data att="1.1"/> 
        </nom-item> 
        <nom-item> 
         <data att="1.2"/> 
        </nom-item> 
       </nom-article> 
      </nom-chapter> 
      <nom-chapter> 
       <nom-article> 
        <data att="2.1"/> 
       </nom-article> 
       <nom-article> 
        <data att="1.1"/> 
       </nom-article> 
      </nom-chapter> 
     </nom-title> 
     <nom-title> 
      <nom-chapter> 
       <nom-article> 
        <data att="1.1"/> 
       </nom-article> 
      </nom-chapter> 
     </nom-title> 
    </sec> 
</root> 

производит разыскиваемого, правильный результат :

<root> 
    <nom-title> 
     <att>1.1</att> 
     <att>1.2</att> 
     <att>2.1</att> 
     <nom-chapter> 
     <att>1.1</att> 
     <att>1.2</att> 
     <nom-article> 
      <att>1.1</att> 
     </nom-article> 
     <nom-article> 
      <att>1.1</att> 
      <att>1.2</att> 
      <nom-item> 
       <data att="1.1"/> 
      </nom-item> 
      <nom-item> 
       <data att="1.2"/> 
      </nom-item> 
     </nom-article> 
     </nom-chapter> 
     <nom-chapter> 
     <att>2.1</att> 
     <att>1.1</att> 
     <nom-article> 
      <att>2.1</att> 
     </nom-article> 
     <nom-article> 
      <att>1.1</att> 
     </nom-article> 
     </nom-chapter> 
    </nom-title> 
    <nom-title> 
     <att>1.1</att> 
     <nom-chapter> 
     <att>1.1</att> 
     <nom-article> 
      <att>1.1</att> 
     </nom-article> 
     </nom-chapter> 
    </nom-title> 
</root> 

Объяснение: Выражая три различных Muenchian группировок, как один, путем динамического построения имя ключа для фактической группировки должны быть выполнены.

Запомнить: Имя ключа является строкой и при необходимости (как в данном случае), имя может быть динамически построено, или передается в качестве параметра.