Използване на XSLT за премахване на дублирани цели в обикновен XML файл

Нов съм в XSLT и имам проблем с премахването на дубликати от обикновен XML файл. Прекарах много време в опити да го получа, но никога не беше съвсем правилно. Ето изходния файл:

<?xml version="1.0" encoding="UTF-16"?>
<language>
    <lang name="welcome">welcom</lang>
    <lang name="open">Open</lang>
    <lang name="close">Close</lang>
    <lang name="welcome">Welcome</lang>
    <lang name="copy">Copy</lang>
</language>

Желаният резултат е следният:

<?xml version="1.0" encoding="UTF-16"?>
<language>
    <lang name="open">Open</lang>
    <lang name="close">Close</lang>
    <lang name="welcome">Welcome</lang>
    <lang name="copy">Copy</lang>
</language>

Действителните файлове са много по-големи от това и "lang" и "name" може да се променят по-късно във файла и искам да запазя само последния дубликат. По принцип, ако етикетът и атрибутите са дублирани, запазете само последния запис. Надявам се това да е възможно с XSLT 1.0. Ако не, винаги мога да използвам множество скриптове, в случай че lang се промени на нещо друго. Благодаря ви предварително!


person CosmicDan    schedule 23.10.2012    source източник
comment
Вие споменавате във въпроса си за атрибути. Можете ли да имате няколко атрибута на елементите си lang или винаги ще бъде само атрибутът name?   -  person Tim C    schedule 23.10.2012
comment
Може да има повече атрибути, но етикетът и атрибутът име са единствените, които трябва да сравня. Другите атрибути, ако се появят такива, също ще бъдат идентични така или иначе.   -  person CosmicDan    schedule 23.10.2012


Отговори (2)


Следният XSLT трябва да отговори на вашия въпрос:

    <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="lang[@name=following-sibling::lang/@name]"/>
</xsl:stylesheet>

По този начин филтрирате всеки lang елемент, който има следващ сродник lang елемент със същата стойност за атрибута name.

person Vincent Biragnet    schedule 23.10.2012

По-общо и много по-ефективно (линейно) решение от квадратичната времева сложност (O(N^2)) на текущо приетия отговор. Това е особено важно при обработката на голям XML документ, тъй като OP ни каза, че действителните документи са:

<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="kLangByName" match="lang" use="@name"/>

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

 <xsl:template match=
 "lang[not(generate-id()
      =
       generate-id(key('kLangByName', @name)[last()]))]"/>
</xsl:stylesheet>

Когато тази трансформация се приложи към предоставения XML документ:

<language>
    <lang name="welcome">welcom</lang>
    <lang name="open">Open</lang>
    <lang name="close">Close</lang>
    <lang name="welcome">Welcome</lang>
    <lang name="copy">Copy</lang>
</language>

желаният правилен резултат се получава:

<language>
   <lang name="open">Open</lang>
   <lang name="close">Close</lang>
   <lang name="welcome">Welcome</lang>
   <lang name="copy">Copy</lang>
</language>

Обяснение:

Използвайки метода на групиране на Muenchian.

person Dimitre Novatchev    schedule 23.10.2012
comment
Хей, благодаря за това. Да, това беше голямо количество данни, но не огромно и не е необходимо да се изпълнява редовно. Но разбирам какво имаш предвид, така че благодаря за съвета за в бъдеще :) - person CosmicDan; 17.04.2014