2010-11-10 4 views
1

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

Ниже урезанная версия 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 -param. Я пробовал следующее:

<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"> петли.)

(примечание стороны: как это наблюдалось в комментарии а) в this page, document() по-видимому, изменяет корень узел, возможно, объясняет такое поведение)

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

Btw: Я попытался определение документа, как этот

<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"> -template фактически где-то во входном документе, а не параметром? (Честно говоря, я довольно смущен, когда дело доходит до контекстных узлов.)

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

+0

+1 хороший вопрос, и, очевидно, хорошее внимание уделялось объяснению и форматированию описания проблемы. – LarsH

+0

Спасибо LarsH! На мой взгляд, вы не получите хороший ответ, если вопрос не будет поставлен правильно. Я надеюсь, что это было не слишком раздутым. – conciliator

ответ

1

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

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

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

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

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

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

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

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

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

Или more precisely,

.

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

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

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

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

Как @Alej намекнул, это действительно невозможно, учитывая вышеуказанное ограничение. Если вы выберете «// Node» в каждой итерации цикла над «$ 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, и ваша таблица стилей говорит версия = "1,0". У вас есть XSLT 2.0? Если да, сообщите нам, и мы можем продолжить этот вариант. Честно говоря, <xsl:document> не знакомая территория для меня. Но я счастлив учиться вместе с тобой.

+0

@LarsH: Вы правы в этой проблеме. Это связано с тем, что использование абсолютного пути '// Node' ('/descendant-or-self :: node()/child :: Node', где сначала **/** означает корень документа). Список создается дважды, потому что на смешанном входе есть два 'RootNode'. – 2010-11-10 13:39:15

+0

@ Alejandro: Я не понимаю, как это могло быть. Какой смешанный вход? Для выражения «// Node» существует только один «/», т. Е. Корневой узел документа контекстного узла. Этот документ, как я понимаю, представляет собой объединенный XML-документ с элементом верхнего уровня TempNode, с двумя детьми RootNode и четырьмя внуками Node. Каждый 'Node' должен быть выбран ровно один раз выражением« // Node ». Наверное, я все еще что-то упускаю. – LarsH

+0

@LarsH: В стилистике OP есть правило, соответствующее 'RootNode'. Этот процесс ** все ** потомком 'Node' с выражением' // Node'. Но в смешанном исходном источнике Op имеется ** два элемента '' RootNode''. – 2010-11-10 16:07:43

0

Вы можете использовать только узлы, необходимо:

Вход:

<?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 
+0

Спасибо за ваш ответ. Тем не менее, я хотел бы подчеркнуть, что обширные изменения в XSLT - это прерыв для меня. Таким образом, ваше решение на самом деле не сократит его для моей потребности, но я уверен, что он выполняет свою работу! ;) – conciliator

Смежные вопросы