Оси XPATH — проблемы

#xslt #xpath #axes

#xslt #xpath #оси

Вопрос:

Структура XML:

 <units>
    <unit>
        <lesson>
            <name>Sample 1</name>
            <sections>
                <section type="IN">
                    <title> Sample Title 1 </title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 2</name>
            <sections>
                <section type="OF">
                    <title> Sample Title 2 </title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 3</name>
            <sections>
                <section type="IN">
                    <title> Sample Title 3</title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 4</name>
            <sections>
                <section type="AS">
                    <title> Sample Title 4</title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 5</name>
            <sections>
                <section type="IN">
                    <title> Sample Title 5</title>
                </section>
            </sections>
        </lesson>
    </unit>
</units>
  

Мое требование состоит в том, чтобы получить значения элемента title и отобразить следующим образом (группировка похожих данных и отображение)

 IN:
Sample Title 1
Sample Title 3
Sample Title 5
OF:
Sample Title 2
AS:
Sample Title 5
  

Я использовал опцию following-sibling, чтобы получить ожидаемый результат. Поскольку структура XML огромна (я вставил только фрагмент), я не могу жестко закодировать путь, используя ../ ../ и все в XSLT. Пожалуйста, помогите мне получить ожидаемый результат.

Комментарии:

1. Группировка с использованием «параметра следующего родственного типа» составляет O (N ^ 2) по временной сложности — это особенно медленно для больших XML-документов, как в вашем случае. Изучите, поймите и используйте более эффективный метод Мюнхена для группировки. Подробнее об этом читайте здесь: jenitennison.com/xslt/grouping/muenchian.html

Ответ №1:

Лучше решить это с помощью группировки, чем любой из родственных осей:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:key name="bySectionType" match="section" use="@type" />
    <xsl:template match="/">
        <xsl:apply-templates select="units/unit/lesson/sections/section" />
    </xsl:template>
    <xsl:template match="section" />
    <xsl:template
        match="section[generate-id()=
                       generate-id(key('bySectionType', @type)[1])]">
        <xsl:value-of select="concat(@type, ':amp;#xA;')" />
        <xsl:apply-templates select="key('bySectionType', @type)" mode="out" />
    </xsl:template>
    <xsl:template match="section" mode="out">
        <xsl:value-of select="concat(normalize-space(title), 'amp;#xA;')" />
    </xsl:template>
</xsl:stylesheet>
  

Вывод:

 IN:
Sample Title 1
Sample Title 3
Sample Title 5
OF:
Sample Title 2
AS:
Sample Title 4
  

Для полноты картины в следующей таблице стилей достигается тот же результат с использованием осей preceding и following :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:template match="/">
        <xsl:apply-templates select="units/unit/lesson/sections/section" />
    </xsl:template>
    <xsl:template match="section" />
    <xsl:template 
        match="section[not(preceding::section[@type=current()/@type])]">
        <xsl:value-of select="concat(@type, ':amp;#xA;')" />
        <xsl:apply-templates select=".|following::section[@type=current()/@type]"
            mode="out" />
    </xsl:template>
    <xsl:template match="section" mode="out">
        <xsl:value-of select="concat(normalize-space(title), 'amp;#xA;')" />
    </xsl:template>
</xsl:stylesheet>
  

Это гораздо менее эффективное решение. Обычный способ решить эту проблему — использовать мюнхенский метод группировки, как показано выше.

Комментарии:

1. @lwburk — Разве мы не можем достичь этого результата, используя оси XPATH?

2. @dirin — Да, но тогда хитрость заключается в том, чтобы определить, когда вы находитесь на первом узле с заданным типом, чтобы вы могли выводить заголовок только для этого узла. Это требует от вас проверки родственных узлов каждого узла, что менее эффективно. (И ваш случай немного отличается из-за вложенности. Вы, вероятно, использовали бы preceding (не preceding-sibling ).)

3. @lwburk — Как я упоминал ранее, фрагмент кода, вставленный в предыдущее сообщение, является <b> частью </b> исходного XML-файла. Я пытался включить ваш код в свой файл XSLT. И есть так много других шаблонов, которые переопределяют это, и я не получаю ожидаемый результат. Есть ли у вас какой-либо другой вариант, кроме этого? Пожалуйста, дайте мне знать.

4. @dirin: Вы могли бы изолировать ответ @lwburk с помощью режимов (даже с режимами в некотором пространстве имен!). Определенный порядок, такой как OF , AS и IN, означал бы, что вы знаете эти ключи заранее. Тогда вам даже не нужна группировка!

5. @dirin: Пожалуйста, задавайте свои новые вопросы как … НОВЫЕ ВОПРОСЫ. Вы получили отличный ответ на свой вопрос — примите его, затем задайте другой вопрос. @lwburk, кто-либо другой или даже сам @Michael-Kay не могут заменить надлежащую дозу начального обучения, которую вы должны предпринять — в этом отношении XSLT не отличается от любого другого языка программирования.

Ответ №2:

Вот решение в XSLT 2.0:

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:template match="/">
        <xsl:for-each-group select="//section" group-by="@type">
           <xsl:value-of select="@type, ':amp;#xa;'" separator=""/>
           <xsl:value-of select="current-group()/title" separator="amp;#xA;" />
           <xsl:value-of select="'amp;#xa;'"/>
        </xsl:for-each-group>              
    </xsl:template>
</xsl:stylesheet>
  

Комментарии:

1. На самом деле мы используем XSLT 1.0. Упомянутая здесь последовательность не относится к порядку возрастания или убывания. Я бы потребовал, чтобы вывод отображался в OF, AS и В (XML может содержать эту информацию в любом порядке)