XSLT: нужна альтернатива document () - функция для обработки из нескольких источников - PullRequest
1 голос
/ 10 ноября 2010

Я адаптирую XSLT от третьей стороны, который преобразует произвольное количество XML в один HTML-документ. Это довольно сложный сценарий, и он будет пересмотрен в будущем, поэтому я пытаюсь сделать минимальную адаптацию, чтобы он работал для наших нужд.

Ниже приведена урезанная версия XSLT (содержащая основы):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
    <xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes"/> 
    <xsl:param name="files" select="document('files.xml')//File"/>
    <xsl:param name="root" select="document($files)"/>
    <xsl:template match="/">
        <xsl:for-each select="$root/RootNode">
            <xsl:apply-templates select="."/>
        </xsl:for-each>
    </xsl:template> 
    <xsl:template match="RootNode">
        <xsl:for-each select="//Node">
            <xsl:text>Node: </xsl:text><xsl:value-of select="."/><xsl:text>, </xsl:text>
        </xsl:for-each>
    </xsl:template> 
</xsl:stylesheet>

Теперь files.xml содержит список всех URL-адресов включаемых файлов (в данном случае это локальные файлы file1.xml и file2.xml). Поскольку мы хотим читать XML-файлы из памяти, а не с диска, и поскольку вызов XSLT допускает только один источник XML, я объединил два файла в один XML-документ. Ниже приведена комбинация двух файлов (в реальной ситуации их может быть больше)

<?xml version="1.0" encoding="UTF-8"?>
<TempNode>
    <RootNode>
        <Node>1</Node>
        <Node>2</Node>
    </RootNode> 
    <RootNode>
        <Node>3</Node>
        <Node>4</Node>  
    </RootNode>
</TempNode>

, где первый RootNode изначально находился в file1.xml, а второй в file2.xml. Из-за сложности самого XSLT я решил, что мой лучший способ - попытаться изменить $root -парам. Я пробовал следующее:

<xsl:param name="root" select="/TempNode"/>

Проблема заключается в следующем. В случае <xsl:param name="root" select="document($files)"/> выражение XPath "//Node" в <xsl:for-each select="//Node"> выбирает узлы из file1.xml и file2.xml независимо, то есть выдает следующий (требуемый) список:

Узел: 1, Узел: 2, Узел: 3, Узел: 4,

Однако, когда я объединяю содержимое двух файлов в один XML и анализирую это (и использую предложенное $root -определение), выражение "//Node" выберет все узлы, которые являются потомками TempNode , (Другими словами, требуемый список, как показано выше, создается дважды из-за комбинации с внешним циклом <xsl:for-each select="$root/RootNode">.)

(примечание: как отмечено в комментарии а) на этой странице , document() явно меняет корневой узел, возможно, объясняя это поведение.)

Мой вопрос становится: Как я могу переопределить $root, используя объединенный XML в качестве источника вместо документа с несколькими источниками через document (), чтобы список создавался только один раз, не затрагивая остальную часть XSLT? Это похоже на то, что если $root определено с помощью функции document(), в параметре нет общего корневого узла. Можно ли определить параметр с двумя "отдельными" деревьями узлов?

Кстати: я пытался определить документ, подобный этому

<xsl:param name="root">
    <xsl:for-each select="/TempNode/*">
        <xsl:document>
            <xsl:copy-of select="."/>
        </xsl:document>
    </xsl:for-each>
</xsl:param>

думал, что это может решить проблему, но выражение "//Node" по-прежнему выбирает все узлы. Является ли узел контекста в шаблоне <xsl:template match="RootNode"> фактически где-то во входном документе, а не в параметре? (Честно говоря, я довольно запутался, когда дело доходит до узлов контекста.)

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 10 ноября 2010

(Обновлено больше)

ОК, часть проблемы становится понятной. Во-первых, просто чтобы убедиться, что я понимаю, вы на самом деле не передаете параметры для $files и $root в вызов процессора XSLT, верно? (Они могут также быть переменными, а не параметрами?)

Теперь к основным вопросам ... В XPath, когда вы оцениваете выражение, начинающееся с "/" (включая "//"), узел контекста игнорируется [в основном] . Поэтому, когда у вас есть

<xsl:template match="RootNode">
    <xsl:for-each select="//Node">

соответствующий RootNode игнорируется. Может быть, вы хотели

<xsl:template match="RootNode">
    <xsl:for-each select=".//Node">

в котором for-each будет выбирать элементы Node, которые являются потомками соответствующего RootNode? Это решило бы вашу проблему создания списка нужных узлов дважды.

Я вставил [в основном] выше, потому что я вспомнил, что «путь абсолютного местоположения» начинается с «корневого узла документа, содержащего узел контекста». Таким образом, контекстный узел влияет на то, какой документ используется для "// узла". Может быть, это то, что вы намеревались все время? Я думаю, что я не спешил, чтобы понять это.

(примечание: как отмечено в комментарии а) на этой странице document () видимо меняет корневой узел, возможно объясняя это поведение.)

или точнее ,

Абсолютный путь к местоположению ["/ ..."] с последующим относительным местоположением путь ... выбирает набор узлов, которые будет выбран родственником путь локации относительно корня узел документа, содержащего контекстный узел .

document() на самом деле ничего не меняет, в смысле побочных эффектов; скорее он возвращает набор узлов, содержащихся (обычно) в разных документах, чем первичный исходный документ. Инструкции XSLT, такие как xsl:apply-templates и xsl:for-each, устанавливают новые значения для узла контекста внутри области действия их шаблонных тел. Так что если вы используете xsl:apply-templates и xsl:for-each с select = "document (...) / ...", узел контекста внутри области действия этих инструкций будет принадлежать внешнему документу, поэтому любое использование "/. .. "как XPath начнется с этого внешнего документа.

Обновлен снова

Как я могу переопределить $ root, используя объединенный XML в качестве источника вместо из нескольких источников через document (), так что список составляется только один раз, не касаясь остальной части XSLT?

Как намекнул @Alej, это действительно невозможно, учитывая вышеуказанное ограничение. Если вы выбираете «// Узел» в каждой итерации цикла над «$ root / RootNode», то для каждой итерации , а не , чтобы выбрать те же узлы, что и другие итерации, каждое значение «$ root / RootNode» должен быть в другом документе. Поскольку вы используете комбинированный источник XML вместо нескольких источников, это невозможно.

Но если вы не настаиваете на том, что ваше <xsl:for-each select="//..."> выражение XPath не может измениться, оно становится очень легким. :-) Просто вставьте "." перед "//".

Это похоже на то, что если $ root определен с помощью функции document (), общего корневого узла нет в парам.

Значение параметра является набором узлов. Все узлы в наборе могут содержаться в одном и том же документе или нет, в зависимости от того, является ли первый аргумент document () набором узлов или просто одним узлом.

Можно ли определить параметр с двумя "отдельными" деревьями узлов?

Я полагаю, под «отдельным» вы подразумеваете «принадлежность к разным документам»? Да, это так, но я не думаю, что вы можете сделать это в XSLT 1.0, если вы сначала не выбираете узлы, которые принадлежат разным документам.

Вы упомянули попытку

<xsl:param name="root">
    <xsl:for-each select="/TempNode/*">
        <xsl:document>
            <xsl:copy-of select="."/>
        </xsl:document>
    </xsl:for-each>
</xsl:param>

, но <xsl:document> не определено в XSLT 1.0, и ваша таблица стилей сообщает version = "1.0". У вас есть XSLT 2.0? Если это так, дайте нам знать, и мы сможем воспользоваться этим вариантом. Если честно, <xsl:document> для меня не знакомая территория. Но я рад учиться вместе с тобой.

0 голосов
/ 10 ноября 2010

Вы можете применять только нужные вам узлы:

Ввод:

<?xml version="1.0" encoding="UTF-8"?>
<TempNode>
    <RootNode>
        <Node>1</Node>
        <Node>2</Node>
    </RootNode> 
    <RootNode>
        <Node>3</Node>
        <Node>4</Node>  
    </RootNode>
</TempNode>


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <xsl:copy>
            <xsl:apply-templates select="TempNode/RootNode"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="RootNode">
        <xsl:value-of select="concat('RootNode-', generate-id(.), '&#10;')"/>
        <xsl:apply-templates select="Node"/>
    </xsl:template>

    <xsl:template match="Node">
        <xsl:value-of select="concat('Node', ., '&#10;')"/>
    </xsl:template>
</xsl:stylesheet>

Выход:

RootNode-N65540
Node1
Node2
RootNode-N65549
Node3
Node4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...