Сравнение 2 наборов узлов на основе последовательности атрибутов - PullRequest
3 голосов
/ 14 июля 2011

Я пытаюсь создать своего рода библиотеку XML, сравнивая различные узлы и объединяя их для последующего повторного использования.Логика должна быть достаточно простой, если последовательность значений атрибутов tag_XX данного языка равна последовательности значений атрибутов tag_YY другого языка, узлы можно объединять.См. Ниже пример XML

<Book>
<Section>
    <GB>
        <Para tag_GB="L1">
            <Content_GB>string_1</Content_GB>
        </Para>
        <Para tag_GB="Illanc">
            <Content_GB>string_2</Content_GB>
        </Para>
        <Para tag_GB="|PLB">
            <Content_GB>string_3</Content_GB>
        </Para>
        <Para tag_GB="L1">
            <Content_GB>string_4</Content_GB>
        </Para>
        <Para tag_GB="Sub">
            <Content_GB>string_5</Content_GB>
        </Para>
        <Para tag_GB="L3">
            <Content_GB>string_6</Content_GB>
        </Para>
        <Para tag_GB="Subbull">
            <Content_GB>string_7</Content_GB>
        </Para>
    </GB>
    <!-- German translations - OK because same attribute sequence -->
    <DE>
        <Para tag_DE="L1">
            <Content_DE>German_translation of_string_1</Content_DE>
        </Para>
        <Para tag_DE="Illanc">
            <Content_DE>German_translation of_string_2</Content_DE>
        </Para>
        <Para tag_DE="|PLB">
            <Content_DE>German_translation of_string_3</Content_DE>
        </Para>
        <Para tag_DE="L1">
            <Content_DE>German_translation of_string_4</Content_DE>
        </Para>
        <Para tag_DE="Sub">
            <Content_DE>German_translation of_string_5</Content_DE>
        </Para>
        <Para tag_DE="L3">
            <Content_DE>German_translation of_string_6</Content_DE>
        </Para>
        <Para tag_DE="Subbull">
            <Content_DE>German_translation of_string_7</Content_DE>
        </Para>
    </DE>
    <!-- Danish translations - NG because not same attribute sequence -->
    <DK>
        <Para tag_DK="L1">
            <Content_DK>Partial_Danish_translation_of_string_1</Content_DK>
        </Para>
        <Para tag_DK="L1_sub">
            <Content_DK>Partial_Danish_translation_of_string_1</Content_DK>
        </Para>
        <Para tag_DK="Illanc">
            <Content_DK>Danish_translation_of_string_2</Content_DK>
        </Para>
        <Para tag_DK="L1">
            <Content_DK>Danish_translation_of_string_4</Content_DK>
        </Para>
        <Para tag_DK="|PLB">
            <Content_DK>Danish_translation_of_string_3</Content_DK>
        </Para>
        <Para tag_DK="L3">
            <Content_DK>Danish_translation_of_string_6</Content_DK>
        </Para>
        <Para tag_DK="Sub">
            <Content_DK>Danish_translation_of_string_5</Content_DK>
        </Para>
        <Para tag_DK="Subbull">
            <Content_DK>Danish_translation_of_string_7</Content_DK>
        </Para>
    </DK>
</Section>
</Book>

Итак

Последовательность значений tag_GB в ГБ = L1 -> Illanc -> ... -> SubBul

Последовательность значений в tag_DE DE = L1 -> Illanc -> ... -> SubBul (так же, как GB, все в порядке)

Последовательность значений tag_DK DK = L1 -> L1.sub -> Упс, ожидаемый Illanc означает, что эта последовательность не совпадает с GB илокаль может быть проигнорирована

Поскольку немецкие и английские наборы узлов имеют одинаковую последовательность атрибутов, я хотел бы объединить их следующим образом:

<Book>
<Dictionary>
    <Para tag="L1">
        <Content_GB>string_1</Content_GB>
        <Content_DE>German_translation of_string_1</Content_DE>
    </Para>
    <Para tag="Illanc">
        <Content_GB>string_2</Content_GB>
        <Content_DE>German_translation of_string_2</Content_DE>
    </Para>
    <Para tag="|PLB">
        <Content_GB>string_3</Content_GB>
        <Content_DE>German_translation of_string_3</Content_DE>
    </Para>
    <Para tag="L1">
        <Content_GB>string_4</Content_GB>
        <Content_DE>German_translation of_string_4</Content_DE>
    </Para>
    <Para tag="Sub">
        <Content_GB>string_5</Content_GB>
        <Content_DE>German_translation of_string_5</Content_DE>
    </Para>
    <Para tag="L3">
        <Content_GB>string_6</Content_GB>
        <Content_DE>German_translation of_string_6</Content_DE>
    </Para>
    <Para tag="Subbull">
        <Content_GB>string_7</Content_GB>
        <Content_DE>German_translation of_string_7</Content_DE>
    </Para>
</Dictionary>
</Book>

Таблица стилей, которую я использую, следующая:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" xmlns="http://www.w3.org/1999/xhtml" encoding="UTF-8" indent="yes"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="text()">
    <xsl:value-of select="normalize-space(.)"/>
</xsl:template>
<xsl:template match="Section">
    <!-- store reference tag list -->
    <xsl:variable name="Ref_tagList" select="GB/Para/attribute()[1]"/>
    <Dictionary>
        <xsl:for-each select="GB/Para">
            <xsl:variable name="pos" select="position()"/>
            <Para tag="{@tag_GB}">
                <!-- Copy English Master -->
                <xsl:apply-templates select="element()[1]"/>
                <xsl:for-each select="//Book/Section/element()[not(self::GB)]">
                    <!-- store current locale tag list -->
                    <xsl:variable name="Curr_tagList" select="Para/attribute()[1]"/>
                    <xsl:if test="$Ref_tagList = $Curr_tagList">
                        <!-- Copy current locale is current tag list equals reference tag list -->
                        <xsl:apply-templates select="Para[position()=$pos]/element()[1]"/>
                    </xsl:if>
                </xsl:for-each>
            </Para>
        </xsl:for-each>
    </Dictionary>
</xsl:template>
</xsl:stylesheet>

Помимо, вероятно, не самого эффективного способа сделать это (я довольно новичок в игре xslt ...), он тоже не работает.Логика, которую я имел в виду, состоит в том, чтобы взять набор атрибутов английского мастера, и, если набор атрибутов любой другой локали равен, я копирую, если не игнорирую.Но по какой-то причине также удачно копируются наборы узлов, которые имеют другую последовательность атрибутов (как показано ниже).Может кто-нибудь сказать мне, где моя логика противоречит реальности?Заранее спасибо!

Токовый выход В том числе датский, который следует игнорировать ...

<Book>
<Dictionary>
    <Para tag="L1">
        <Content_GB>string_1</Content_GB>
        <Content_DE>German_translation of_string_1</Content_DE>
        <Content_DK>Partial_Danish_translation_of_string_1</Content_DK>
    </Para>
    <Para tag="Illanc">
        <Content_GB>string_2</Content_GB>
        <Content_DE>German_translation of_string_2</Content_DE>
        <Content_DK>Partial_Danish_translation_of_string_1</Content_DK>
    </Para>
    <Para tag="|PLB">
        <Content_GB>string_3</Content_GB>
        <Content_DE>German_translation of_string_3</Content_DE>
        <Content_DK>Danish_translation_of_string_2</Content_DK>
    </Para>
    <Para tag="L1">
        <Content_GB>string_4</Content_GB>
        <Content_DE>German_translation of_string_4</Content_DE>
        <Content_DK>Danish_translation_of_string_4</Content_DK>
    </Para>
    <Para tag="Sub">
        <Content_GB>string_5</Content_GB>
        <Content_DE>German_translation of_string_5</Content_DE>
        <Content_DK>Danish_translation_of_string_3</Content_DK>
    </Para>
    <Para tag="L3">
        <Content_GB>string_6</Content_GB>
        <Content_DE>German_translation of_string_6</Content_DE>
        <Content_DK>Danish_translation_of_string_6</Content_DK>
    </Para>
    <Para tag="Subbull">
        <Content_GB>string_7</Content_GB>
        <Content_DE>German_translation of_string_7</Content_DE>
        <Content_DK>Danish_translation_of_string_5</Content_DK>
    </Para>
</Dictionary>
</Book>

Ответы [ 2 ]

1 голос
/ 14 июля 2011

Эта таблица стилей использует <xsl:for-each-group>

  1. Сначала группирует элементы по последовательности Para/@* значений
  2. Затем для каждой из этих последовательностей группирует Para используя количество элементов following sibling, которые имеют атрибуты, начинающиеся с «тега».

У меня есть фильтры предикатов для совпадений для @*, чтобы гарантировать, что он сравнивает те, которыеначать с "tag_".В этом может не быть необходимости, но это поможет обеспечить его работоспособность, если к экземпляру XML будут добавлены другие атрибуты.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" xmlns="http://www.w3.org/1999/xhtml" encoding="UTF-8"
        indent="yes"/>
    <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="text()" priority="1">
        <xsl:value-of select="normalize-space(.)"/>
    </xsl:template>

    <xsl:template match="Section">
        <xsl:for-each-group select="*"
            group-adjacent="string-join(
            Para/@*[starts-with(local-name(),'tag_')],'|')">
            <Dictionary>
                <xsl:for-each-group select="current-group()/Para"
                    group-by="count(
                    following-sibling::*[@*[starts-with(local-name(),'tag_')]])">
                    <Para tag="{(current-group()/@*[starts-with(local-name(),'tag_')])[1]}">
                        <xsl:copy-of select="current-group()/*"/>
                    </Para>
                </xsl:for-each-group>
            </Dictionary>
        </xsl:for-each-group>
    </xsl:template>

</xsl:stylesheet>

При применении к примеру входного XML-файла выдает следующий вывод:

<Book>
   <Dictionary>
      <Para tag="L1">
         <Content_GB>string_1</Content_GB>
         <Content_DE>German_translation of_string_1</Content_DE>
      </Para>
      <Para tag="Illanc">
         <Content_GB>string_2</Content_GB>
         <Content_DE>German_translation of_string_2</Content_DE>
      </Para>
      <Para tag="|PLB">
         <Content_GB>string_3</Content_GB>
         <Content_DE>German_translation of_string_3</Content_DE>
      </Para>
      <Para tag="L1">
         <Content_GB>string_4</Content_GB>
         <Content_DE>German_translation of_string_4</Content_DE>
      </Para>
      <Para tag="Sub">
         <Content_GB>string_5</Content_GB>
         <Content_DE>German_translation of_string_5</Content_DE>
      </Para>
      <Para tag="L3">
         <Content_GB>string_6</Content_GB>
         <Content_DE>German_translation of_string_6</Content_DE>
      </Para>
      <Para tag="Subbull">
         <Content_GB>string_7</Content_GB>
         <Content_DE>German_translation of_string_7</Content_DE>
      </Para>
   </Dictionary>
   <Dictionary>
      <Para tag="L1">
         <Content_DK>Partial_Danish_translation_of_string_1</Content_DK>
      </Para>
      <Para tag="L1_sub">
         <Content_DK>Partial_Danish_translation_of_string_1</Content_DK>
      </Para>
      <Para tag="Illanc">
         <Content_DK>Danish_translation_of_string_2</Content_DK>
      </Para>
      <Para tag="L1">
         <Content_DK>Danish_translation_of_string_4</Content_DK>
      </Para>
      <Para tag="|PLB">
         <Content_DK>Danish_translation_of_string_3</Content_DK>
      </Para>
      <Para tag="L3">
         <Content_DK>Danish_translation_of_string_6</Content_DK>
      </Para>
      <Para tag="Sub">
         <Content_DK>Danish_translation_of_string_5</Content_DK>
      </Para>
      <Para tag="Subbull">
         <Content_DK>Danish_translation_of_string_7</Content_DK>
      </Para>
   </Dictionary>
</Book>
1 голос
/ 14 июля 2011

Возможно, это не лучшее решение. Я использовал следующие функции XSLT 2.0:

  • Я сравнил последовательность атрибутов, используя string-join().
  • Я использовал возможность использования переменных RTF

Возможно, существует больше возможностей XSLT 2.0, которые могут решить вашу проблему. но я думаю, что БОЛЬШАЯ проблема здесь - ваш входной документ.

Извините, я не посмотрел ваше текущее преобразование. Только что реализовал один с нуля. Надеюсь, это поможет:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="GB">
        <Book>
            <Dictionary>

                <xsl:variable name="matches">
                    <xsl:for-each select="following-sibling::*
                        [string-join(Para/@*,'-')
                        = string-join(current()/Para/@*,'-')]">
                        <match><xsl:copy-of select="Para/*"/></match>
                    </xsl:for-each>
                </xsl:variable>

                <xsl:apply-templates select="Para">
                    <xsl:with-param name="matches" select="$matches"/>
                </xsl:apply-templates>

            </Dictionary>
        </Book>
    </xsl:template>

    <xsl:template match="Para[parent::GB]">
        <xsl:param name="matches"/>
        <xsl:variable name="pos" select="position()"/>
        <Para tag="{@tag_GB}">
            <xsl:copy-of select="Content_GB"/>
            <xsl:copy-of select="$matches/match/*[position()=$pos]"/>
        </Para>
    </xsl:template>

    <xsl:template match="text()"/>

</xsl:stylesheet> 

При применении к входному документу, представленному в вопросе, получается следующий вывод:

<Book>
   <Dictionary>
      <Para tag="L1">
         <Content_GB>string_1</Content_GB>
         <Content_DE>German_translation of_string_1</Content_DE>
      </Para>
      <Para tag="Illanc">
         <Content_GB>string_2</Content_GB>
         <Content_DE>German_translation of_string_2</Content_DE>
      </Para>
      <Para tag="|PLB">
         <Content_GB>string_3</Content_GB>
         <Content_DE>German_translation of_string_3</Content_DE>
      </Para>
      <Para tag="L1">
         <Content_GB>string_4</Content_GB>
         <Content_DE>German_translation of_string_4</Content_DE>
      </Para>
      <Para tag="Sub">
         <Content_GB>string_5</Content_GB>
         <Content_DE>German_translation of_string_5</Content_DE>
      </Para>
      <Para tag="L3">
         <Content_GB>string_6</Content_GB>
         <Content_DE>German_translation of_string_6</Content_DE>
      </Para>
      <Para tag="Subbull">
         <Content_GB>string_7</Content_GB>
         <Content_DE>German_translation of_string_7</Content_DE>
      </Para>
   </Dictionary>
</Book>
...