Объединить несколько узлов с вложенными узлами - PullRequest
2 голосов
/ 23 марта 2011

Я новичок в XSL, но мне нужен XSL-файл, который объединяет узлы с одним и тем же атрибутом имени в один элемент - и то же самое, что и для узлов сына - и если есть узлы с разными именами, они ставятся как Пример XML выглядит следующим образом.

<?xml version="1.0" encoding="UTF-8"?>
<test>
    <component name="root">
        <component name="c2">
            <component name="c3">
                <component name="c4" />
            </component>
        </component>
    </component>
    <component name="root">
        <component name="c2">
            <component name="A4" />
        </component>
        <component name="root">
            <component name="A3" />
        </component>
        <component name="root">
            <component name="X1">
                <component name="X2" />
            </component>
        </component>
    </component>
    <component name="difRoot">
    </component>
</test>

Требуемый вывод выглядит следующим образом

<output>
<component name="root">
    <component name="c2">
        <component name="c3">
            <component name="c4"/>
        </component>
        <component name="A4"/>
    </component>
    <component name="A3"/>
          <component name="X1">
                 <component name="X2"/>
          </component>
</component>
<component name="difRoot"/>
</output>

Спасибо

1 Ответ

2 голосов
/ 23 марта 2011

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kComponentByName" match="component" use="@name"/>
    <xsl:template match="test">
        <output>
            <xsl:call-template name="merge">
                <xsl:with-param name="pSequence" select="*"/>
            </xsl:call-template>
        </output>
    </xsl:template>
    <xsl:template name="merge">
        <xsl:param name="pSequence" select="/.."/>
        <xsl:if test="$pSequence">
            <xsl:variable name="vName" select="$pSequence[1]/@name"/>
            <xsl:for-each select="$pSequence[1]">
                <xsl:copy>
                    <xsl:copy-of select="@*"/>
                    <xsl:call-template name="merge">
                        <xsl:with-param name="pSequence"
                             select="key('kComponentByName',$vName)
                                        /component[@name != $vName]"/>
                    </xsl:call-template>
                </xsl:copy>
            </xsl:for-each>
            <xsl:call-template name="merge">
                <xsl:with-param name="pSequence"
                     select="$pSequence[@name != $vName]"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Выход:

<output>
    <component name="root">
        <component name="c2">
            <component name="c3">
                <component name="c4" />
            </component>
            <component name="A4" />
        </component>
        <component name="A3" />
        <component name="X1">
            <component name="X2" />
        </component>
    </component>
    <component name="difRoot" />
</output>

Примечание : рекурсивная иерархия не полностью решена, только предотвращает себя как ребенка.

Обновление : объяснение именованного шаблона: если pSequence не пустой, возьмите первый узел, затем скопируйте себя и примените шаблоны к дочерним элементам всех элементов component с одинаковыми @name ( отфильтровывать тех с этим @name тоже); наконец, вызовите себе фильтрацию pSequence элементов component с тем же @name, что и обработанным. Таким образом, он идет уровень за уровнем и узел за узлом, фильтруя братьев и сестер и детей. Полное предотвращение цикличности должно быть сделано, передавая последовательность с именами предков для фильтрации. Это оставлено как упражнение ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...