2012-06-12 2 views
3

Я хочу, чтобы выходной файл xml был сгруппирован для элемента 'c', в соответствии с атрибутом 'f'. Вот мой ввод xml и xslt. Я хочу, чтобы группа возникла только один раз, а остальные узлы должны быть скопированы так же, как и вывод. Xslt я попытался, копирует весь входной XML-файл. Поэтому, если есть два или более элемента с элементом c и тем же значением атрибута для 'f', хотите, чтобы первое появление этой группы было на выходе. Мой желаемый результат также скопирован.Преобразование для удаления дубликатов и копирования остатка

ввод XML

<M> 
    <a> 
     <b> 
     <c f="123"> 
      <d>Al</d> 
      <e NO="678"> 
       <f>Y</f> 
       <g> 
        <h>FTO</h> 
       </g> 
      </e> 
     </c> 
     </b> 
    </a> 
    <a> 
    <b> 
     <c f="123"> 
     <d>Al</d> 
     <e NO="678"> 
      <f>Y</f> 
      <g> 
      <h>FTO</h> 
      </g> 
     </e> 
     </c> 
    </b> 
    </a> 
    <a> 
    <b> 
     <c f="567"> 
     <d>Al</d> 
     <e NO="678"> 
      <f>Y</f> 
      <g> 
      <h>FTO</h> 
      </g> 
     </e> 
     </c> 
    </b> 
    </a> 
    <a> 
    <b> 
     <somethingelse></somethingelse> 
    </b> 
    </a> 
</M> 

хотел вывода XML

<M> 
    <a> 
    <b> 
     <c f="123"> 
     <d>Al</d> 
     <e NO="678"> 
      <f>Y</f> 
      <g> 
      <h>FTO</h> 
      </g> 
     </e> 
     </c> 
    </b> 
    </a> 
    <a> 
    <b> 
     <c f="567"> 
     <d>Al</d> 
     <e NO="678"> 
      <f>Y</f> 
      <g> 
      <h>FTO</h> 
      </g> 
     </e> 
     </c> 
    </b> 
    </a> 
    <a> 
    <b> 
     <somethingelse></somethingelse> 
    </b> 
    </a> 
</M> 

XSLT я попытался

<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="mykey" match="c" 
    use="@f"/> 

    <xsl:template match= 
    "c[generate-id() 
     = 
     generate-id(key('mykey',@f)[1]) 
     ] 
    "> 



    <xsl:text/> 
    <xsl:copy-of select="key('mykey',@f)[1]"/> 
    </xsl:template> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

ответ

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="kAByC-F" match="a" use="*/c/@f"/> 

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

    <xsl:template match= 
     "a[*/c 
     and 
     not(generate-id() 
      = 
      generate-id(key('kAByC-F', */c/@f)[1]) 
      ) 
     ]"/> 
</xsl:stylesheet> 

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

<M> 
    <a> 
     <b> 
     <c f="123"> 
      <d>Al</d> 
      <e NO="678"> 
       <f>Y</f> 
       <g> 
        <h>FTO</h> 
       </g> 
      </e> 
     </c> 
     </b> 
    </a> 
    <a> 
    <b> 
     <c f="123"> 
     <d>Al</d> 
     <e NO="678"> 
      <f>Y</f> 
      <g> 
      <h>FTO</h> 
      </g> 
     </e> 
     </c> 
    </b> 
    </a> 
    <a> 
    <b> 
     <c f="567"> 
     <d>Al</d> 
     <e NO="678"> 
      <f>Y</f> 
      <g> 
      <h>FTO</h> 
      </g> 
     </e> 
     </c> 
    </b> 
    </a> 
    <a> 
    <b> 
     <somethingelse></somethingelse> 
    </b> 
    </a> 
</M> 

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

<M> 
    <a> 
     <b> 
     <c f="123"> 
      <d>Al</d> 
      <e NO="678"> 
       <f>Y</f> 
       <g> 
        <h>FTO</h> 
       </g> 
      </e> 
     </c> 
     </b> 
    </a> 
    <a> 
     <b> 
     <c f="567"> 
      <d>Al</d> 
      <e NO="678"> 
       <f>Y</f> 
       <g> 
        <h>FTO</h> 
       </g> 
      </e> 
     </c> 
     </b> 
    </a> 
    <a> 
     <b> 
     <somethingelse/> 
     </b> 
    </a> 
</M> 

Объяснение:

Правильное использование Muenchian grouping method.

+0

Удивительное решение :) – Suresh

+0

@Suresh: Добро пожаловать. –

0

Одна идея может быть, чтобы сохранить все значения с в переменной в формате, позволяет различать их друг от друга, а затем каждый раз, когда вы сталкиваетесь с, вы проверяете, имеет ли это значение c содержащихся внутри переменной. Если это так, перейдите к следующему узлу. Если это не так, продолжайте обработку текущего узла.

Скажите мне, если вам нужна более конкретная информация

EDIT: В качестве альтернативы, и, возможно, более простой метод (я использую NAnt в последнее время, так что я мог бы дать вам стратегию NAnt) является своего рода все узлы по их значениям s. Затем просто введите переменную, которая сохраняет текущее значение c и сравнивается, пока значение, которое вы ищете, не равно сохраненному значению. Затем переназначьте значение и сделайте это снова!

1

простое решения было бы просто добавить пустой шаблон для всех последующих c узлов:

<xsl:template match="c[generate-id() = generate-id(key('mykey',@f)[position() &gt; 1])]" /> 
+0

Только это удаляет повторяющиеся элементы 'c', однако задача состоит в том, чтобы удалить полное поддерево, внедренное в предка' a' каждого дубликата. Это то, что дает решение в моем ответе. –

+0

Хм ... Мне нужно будет подумать об этом чуть больше (я все еще Muenchian Grouping-challenged, но хочу учиться). Самое забавное, что он, кажется, производит желаемый результат. Btw: Ваш (показанный) вывод пропускает часть, содержащую '' (еще не пробовал ваш xslt). – Filburt

+0

Спасибо за хорошее наблюдение - исправлено. –

0

Вы можете соответствовать <a> элементам и проверить, есть ли какие-либо предыдущие братья с тем же атрибутом f в их <c> подразделах -элементов. Если это так, вы обнаружили дубликат заданного значения f (вхождение данного значения f, которое не является первым вхождением этого значения), и вы можете просто переопределить шаблон идентификации, чтобы пропустить элемент:

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

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

    <xsl:template match="/M/a[b/c/@f = preceding-sibling::a/b/c/@f]"/> 

</xsl:stylesheet> 

Преимущество этого решения в том, что он не требует каких-либо знаний о генерации ключа или идентификатора; он просто работает с основными функциями оси XPath. Тем не менее, это может немного усложниться, когда элементы для сравнения не все находятся на одной и той же глубине вложенности/в той же иерархии относительных элементов.

PS: Я удалил элемент <xsl:strip-space elements="*"/>, потому что я не смог его протестировать (мой процессор Xml утверждал, что я могу использовать его только в том случае, если передаю читаемый поток, а не файл), но не забудьте его снова вставить, если он работает для вас.

+2

Не могли бы вы заменить этот весь второй шаблон на ''? –

+0

@DevNull: Возможно. Я думаю, что я столкнулся и прочитал что-то о выражениях XPath в атрибутах 'match', не поддерживающих некоторые ограничения. Теперь я не могу найти больше, но это дало мне головную боль: -/ –

+0

@DevNull: Вы правы; то, что я помню, пришлось делать исключительно с использованием значений параметров. Я упростил код в своем ответе. Спасибо. –

Смежные вопросы