Получение значений из узлов, когда сам XPath представляет собой текст внутри узла

Простая проблема: у меня есть такой файл XML:

<Locations>
    <Location>/Simulation/@ID</Location>
    <Location>/Simulation/Loans</Location>
    <Location>/Simulation/Assets</Location>
    <Location>/Simulation/BankAssets</Location>
    <Location>/Simulation/RealEstates</Location>
</Locations>

У меня также есть второй файл XML, содержащий данные, соответствующие этим узлам XPath. И мне нужно создать XSLT 1.0 без каких-либо сценариев, которые могут преобразовать этот файл данных, чтобы просто создать список данных, соответствующих этим узлам. Что-то вроде этого:

<Data>
    <Item Node="/Simulation/@ID">
        <Value>1</Value>
        <Value>2</Value>
        <Value>3</Value>
    </Item>
    <Item Node="/Simulation/Loans">
        <Value>1024</Value>
        <Value>555</Value>
        <Value>0</Value>
    </Item>
</Data>

Количество узлов Value на элемент не имеет значения. Соотношение между ценностями различных предметов также не имеет значения. По сути, таблица стилей предназначена только для сбора простых статистических данных для суммирования, усреднения и прочего. Список локаций фургона сильно различается в зависимости от того, что хочет пользователь. Этот пример — просто поддельные данные.

Вопрос: как собрать эту информацию?


person Wim ten Brink    schedule 13.04.2011    source источник
comment


Ответы (1)


На самом деле есть два способа сделать это: один — выполнить двухфазное преобразование, а другой — использовать функцию расширения evaluate.

Двухфазное преобразование

Сначала вам нужно сгенерировать правильный XSL на основе вашего списка Locations. Это может выглядеть так:

<xsl:template match="/">
    <Data>
        <Item Node="/Simulation/@ID">
            <xsl:apply-templates select="/Simulation/@ID"/>
        </Item>
        <Item Node="/Simulation/Loans">
            <xsl:apply-templates select="/Simulation/Loans"/>
        </Item>

        <!-- ... and so on ... -->

    </Data>
</xsl:template>

<xsl:template match="node()|@*">
    <Value>
        <xsl:value-of select="."/>
    </Value>
</xsl:template>

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

Следующим шагом является применение сгенерированного преобразования к исходному файлу. Вы можете объединить эти два шага в небольшую трубку и получить желаемые результаты.

Использование функции расширения

Библиотека EXSLT содержит функцию расширения, называемую evaluate, которая помогает в таких случаях, как ваш. Он поддерживается трансформатором Xalan из коробки, но не Saxon, насколько я знаю. Однако с этой функцией существует серьезная проблема: начиная с версии 2.7 Xalan существует ошибка, который предотвращает выполнение нескольких вычислений. К сожалению, приведенная ниже таблица стилей была затронута, когда я попытался ее запустить. Предлагается изменить версию Xalan на 2.6, если это возможно. Тем не менее, вот таблица стилей, которая сделает то, что вы хотите, без дополнительной фазы генерации.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dyn="http://exslt.org/dynamic" extension-element-prefixes="dyn">

    <xsl:variable name="locations" select="document('xpath.xml')/Locations"/>
    <xsl:variable name="root" select="/"/>

    <xsl:template match="/">
        <Data>
            <xsl:for-each select="$locations/Location">
                <Item Node="{.}">
                    <xsl:variable name="currentLocation" select="concat('$root', .)"/>
                    <xsl:apply-templates select="dyn:evaluate($currentLocation)"/>
                </Item>
            </xsl:for-each>
        </Data>
    </xsl:template>

    <xsl:template match="node()|@*">
        <Value>
            <xsl:value-of select="."/>
        </Value>
    </xsl:template>

</xsl:stylesheet>

Предполагается, что документ с определениями Locations находится в том же каталоге и называется xpath.xml. Обновите использование функции document(), если это не так.

person oiavorskyi    schedule 13.04.2011
comment
Существуют также реализации только XSLT 1.0 простых шаблонов обходчика выражений XPath, подобных тем, которые я публикую в комментарии к вопросу. - person ; 14.04.2011
comment
К сожалению, EXSLT не вариант. - person Wim ten Brink; 13.05.2011