Невозможно выбрать элемент в результате node-set(), используя имя, но * находит его
У меня есть таблица стилей XSLT 1.0*, которая выполняет некоторую предварительную обработку и создает результирующий фрагмент, состоящий из списка элементов <x>
у каждого из которых двое детей - тогда позвоним <a>
а также <b>
,
Таким образом, сгенерированный список выглядит так:
<x><a>A-content</a><b>B-content</b></x>
<x><a>A-content</a><b>B-content</b></x>
...
<x><a>A-content</a><b>B-content</b></x>
Затем я преобразую это в набор узлов с помощью node-set() и использую apply-templates для преобразования всех <x>
элементы для выходного представления.
Все идет нормально.
Но я должен использовать match="*"
правило для выходного шаблона, и хотя я могу получить дочерние элементы, используя "*[1]"
а также "*[2]"
Я не могу найти их, используя "a"
а также "b"
- Я просто получаю пустой результат.
Позиционный синтаксис работает как обходной путь, но он довольно хрупкий, и я хотел бы вернуться к работе с именами элементов. Кроме того, это не очень читабельно.
Я подозревал, что это может быть проблема с пространством имен (<x>
, <a>
а также <b>
не определены в исходной схеме для входных или выходных документов), но, насколько я вижу, нет декорирования пространства имен, когда элементы выбираются с помощью "*".
На всякий случай это важно, я использую xsltproc в Cygwin (libxml 20902, libxslt 10128 и libexslt 817).
Любые идеи о том, что я могу делать не так, или советы по отладке?
(* - Я должен использовать XSLT 1.0, потому что он предназначен для работы в веб-браузере.)
РЕДАКТИРОВАТЬ: добавлены примеры, как требуется
Входной файл test.xml:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="test.xsl" type="text/xsl" ?>
<books>
<book>
<title>Diaspora</title>
<author>Greg Egan</author>
</book>
<book>
<title>2001</title>
<author>Arthur C Clarke</author>
</book>
<book>
<title>Eon</title>
<author>Greg Bear</author>
</book>
</books>
Преобразуйте test.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"
xmlns:exslt="http://exslt.org/common"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:msxslt="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="xsl msxslt exslt xalan">
<!-- extension-element-prefixes="exslt"> -->
<xsl:template match="books">
<!-- Generate list -->
<xsl:variable name="list">
<xsl:apply-templates select="book" mode="phase1"/>
</xsl:variable>
<html>
<head>
<title>Books</title>
</head>
<body>
<xsl:choose>
<xsl:when test="function-available('msxslt:node-set')">
<xsl:apply-templates select="msxslt:node-set($list)" mode="process-list"/>
</xsl:when>
<xsl:when test="function-available('exslt:node-set')">
<xsl:apply-templates select="exslt:node-set($list)" mode="process-list"/>
</xsl:when>
<xsl:when test="function-available('xalan:nodeset')">
<xsl:apply-templates select="xalan:nodeset($list)" mode="process-list"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$list" mode="process-list"/>
</xsl:otherwise>
</xsl:choose>
</body>
</html>
</xsl:template>
<xsl:template match="book" mode="phase1">
<!-- Actual transformation is more involved -->
<xsl:element name="x">
<xsl:element name="a">
<b>
<xsl:value-of select="author/text()"/>
</b>
</xsl:element>
<xsl:element name="b">
<i>
<xsl:value-of select="title/text()"/>
</i>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="*" mode="process-list">
<p>
[<xsl:value-of select="*[1]"/>]
[<xsl:value-of select="*[2]"/>]
[<xsl:value-of select="a"/>]
[<xsl:value-of select="b"/>]
</p>
</xsl:template>
</xsl:stylesheet>
Вывод (один и тот же вывод из msxslt и xsltproc):
<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>Books</title></head>
<body>
<p>
[Greg Egan]
[Diaspora]
[]
[]
</p><p>
[Arthur C Clarke]
[2001]
[]
[]
</p><p>
[Greg Bear]
[Eon]
[]
[]
</p>
</body>
</html>
2 ответа
Я продолжил поиск и нашел решение. Как мы и подозревали, это была проблема с пространством имен - вот предыдущий пост с описанием этого.
Несмотря на мои попытки поместить новые элементы в новое пространство имен, они все еще входили в пространство имен по умолчанию, которое я объявил как:
xmlns="http://www.w3.org/1999/xhtml"
Это, однако, не рассматривается как выражение по умолчанию в выражении XPATH, поэтому оно не было найдено. (Вторичный вопрос - почему бы и нет?)
Решением было повторить объявление пространства имен по умолчанию с префиксом пространства имен:
xmlns:xhtml="http://www.w3.org/1999/xhtml"
и используйте этот префикс явно в xpath:
[<xsl:copy-of select="xhtml:a"/>]
[<xsl:copy-of select="xhtml:b"/>]
Тогда все совпадает, и я получаю идентичный вывод из именованных и позиционных выражений XPATH.
Спасибо всем за то, что вы выступаете в качестве декорации - надеюсь, это поможет кому-то еще позже
Я могу только предположить, что вы используете расширение exslt для создания набора узлов, и я предполагаю, что вы пытаетесь настроить многопроходное преобразование:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:template match="/">
<!-- create your first pass here -->
<xsl:variable name="first-pass">
<xsl:apply-templates mode="first-pass"/>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($first-pass)" mode="second-pass"/>
</xsl:template>
<!-- implementation of first-pass
....
-->
<!-- second-pass: find a and b elements in x -->
<xsl:template match="x/a" mode="second-pass">
<!-- your turn -->
</xsl:template>
<xsl:template match="x/b" mode="second-pass">
<!-- your turn -->
</xsl:template>
<xsl:template match="@*|node()" mode="second-pass">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="second-pass"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()" mode="first-pass">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="first-pass"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>