Преобразование XML в обычный текст-как игнорировать / обрабатывать пробелы в XSLT?
Я пытаюсь преобразовать XML-файл в разметку, используемую dokuwiki, используя XSLT. Это действительно работает в некоторой степени, но отступ в файле XSL вставляется в результаты. На данный момент у меня есть два варианта: полностью отказаться от этой вещи XSLT и найти другой способ конвертировать из XML в разметку dokuwiki или удалить около 95% пробелов из файла XSL, что делает его почти нечитаемым и кошмаром обслуживания.
есть ли способ сохранить отступ в xsl-файле без передачи всех этих пробелов в окончательный документ?
фон: я переношу инструмент autodoc со статических HTML-страниц на dokuwiki, поэтому API, разработанный командой сервера, может быть дополнительно документирован командой приложений всякий раз, когда команда приложений сталкивается с плохо документированным кодом. Логика состоит в том, чтобы иметь раздел каждой страницы, отведенный для инструмента autodoc, и разрешить комментарии в любом месте за пределами этого блока. Я использую XSLT, потому что мы уже есть файл XSL для преобразования из XML в XHTML, и я предполагаю, что будет быстрее переписать XSL, чем свернуть мое собственное решение с нуля.
Edit: Ах, правильно, глупый я, я пренебрег атрибутом отступа. (Другое фоновое примечание: Я новичок в XSLT. С другой стороны, мне все еще приходится иметь дело с новыми линиями. Dokuwiki использует каналы для различения столбцов таблицы, что означает, что все данные в строке таблицы должны быть в одной строке. Есть ли способ подавить выводятся новые строки (только иногда), поэтому я могу сделать довольно сложную логику для каждой ячейки таблицы в несколько читаемом fasion?
4 ответов
есть три причины для получения нежелательных пробелов в результате преобразования XSLT:
- пробелы, которые поступают между узлами в исходном документе
- пробелы, которые поступают из узлов в исходном документе
- пробел, который исходит из таблицы стилей
я собираюсь поговорить обо всех трех, потому что может быть трудно сказать, откуда берутся пробелы, поэтому вам может потребоваться использовать несколько стратегия.
чтобы обратиться к пробелам, которые находятся между узлами в исходном документе, вы должны использовать <xsl:strip-space>
чтобы удалить любые пробелы, которые появляются между двумя узлами, а затем использовать <xsl:preserve-space>
чтобы сохранить значительные пробелы, которые могут появиться в смешанном содержимом. Например, если исходный документ выглядит следующим образом:
<ul>
<li>This is an <strong>important</strong> <em>point</em></li>
</ul>
тогда вы захотите игнорировать пробелы между <ul>
и <li>
и между </li>
и </ul>
, что не имеет значения, но сохраняет пробелы между <strong>
и <em>
элементы, которые is значительный (в противном случае вы получите "это **важный***пункт*"). Для этого используйте
<xsl:strip-space elements="*" />
<xsl:preserve-space elements="li" />
на elements
атрибут on <xsl:preserve-space>
должен в основном перечислять все элементы в вашем документе, которые имеют смешанное содержимое.
в сторону: использование
<xsl:strip-space>
также уменьшает размер исходного дерева в памяти и делает вашу таблицу стилей больше эффективно, поэтому это стоит сделать, даже если у вас нет проблем с пробелами такого рода.
чтобы обратиться к пробелам, которые появляются в узлах в исходном документе, вы должны использовать normalize-space()
. Например, если у вас есть:
<dt>
a definition
</dt>
и вы можете быть уверены в том, что <dt>
элемент не будет содержать элементов, с которыми вы хотите что-то сделать, тогда вы можете сделать:
<xsl:template match="dt">
...
<xsl:value-of select="normalize-space(.)" />
...
</xsl:template>
начальное и конечное пробелы будут удалены из значения the <dt>
элемент, и вы просто получите строку "a definition"
.
для решения пробелов, исходящих из таблицы стилей, которая, возможно, является той, которую вы испытываете, когда у вас есть текст в шаблоне, как это:
<xsl:template match="name">
Name:
<xsl:value-of select="." />
</xsl:template>
таблицы стилей XSLT анализируются так же, как и исходные документы, которые они обрабатывают, поэтому приведенный выше XSLT интерпретируется как дерево, содержащее <xsl:template>
элемент match
атрибут, чей первый дочерний элемент является текстовым узлом и чей второй ребенок-это <xsl:value-of>
элемент . Текстовый узел имеет начальный и конечный пробелы (включая разрывы строк); поскольку это буквальный текст в таблице стилей, он буквально копируется в результат со всеми начальными и конечными пробелами.
но некоторые пробелы в таблицах стилей XSLT удаляются автоматически, а именно между узлами. Вы не получаете разрыв строки в своем результате, потому что есть разрыв строки между <xsl:value-of>
и в конце <xsl:template>
.
чтобы получить только текст, который вы хотите в результате, используйте <xsl:text>
элемент такой:
<xsl:template match="name">
<xsl:text>Name: </xsl:text>
<xsl:value-of select="." />
</xsl:template>
процессор XSLT будет игнорировать разрывы строк и отступы, которые появляются между узлами, и выводить только текст в <xsl:text>
элемент.
вы используете indent= " no " в своем выходном теге?
<xsl:output method="text" indent="no" />
также, если вы используете xsl: value-of вы можете использовать disable-output-escaping= "yes", чтобы помочь с некоторыми проблемами пробелов.
ответ@JeniT отличный, я просто хочу указать на трюк для управления пробелами. Я не уверен, что это лучший способ (или даже хороший способ), но это работает для меня сейчас.
("С" - пространство, "е" - пустой, "н" для перехода на новую строку.)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:transform [
<!ENTITY s "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" >
<!ENTITY s2 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" >
<!ENTITY s4 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" >
<!ENTITY s6 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" >
<!ENTITY e "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'></xsl:text>" >
<!ENTITY n "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
</xsl:text>" >
]>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
&e;Flush left, despite the indentation.&n;
&e; This line will be output indented two spaces.&n;
<!-- the blank lines above/below won't be output -->
<xsl:for-each select="//foo">
&e; Starts with two blanks: <xsl:value-of select="@bar"/>.&n;
&e; <xsl:value-of select="@baz"/> The 'e' trick won't work here.&n;
&s2;<xsl:value-of select="@baz"/> Use s2 instead.&n;
&s2; <xsl:value-of select="@abc"/> <xsl:value-of select="@xyz"/>&n;
&s2; <xsl:value-of select="@abc"/>&s;<xsl:value-of select="@xyz"/>&n;
</xsl:for-each>
</xsl:template>
</xsl:transform>
применяется для:
<?xml version="1.0" encoding="UTF-8"?>
<foo bar="bar" baz="baz" abc="abc" xyz="xyz"></foo>
выходы:
Flush left, despite the indentation.
This line will be output indented two spaces.
Starts with two blanks: bar.
baz The 'e' trick won't work here.
baz Use s2 instead.
abcxyz
abc xyz
трюк " e " работает до текстового узла, содержащего по крайней мере один символ без пробелов, потому что он расширяется до этого:
<xsl:template match="/">
<xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text>
</xsl:text>
С the правила зачистки пробелов скажем, что текстовые узлы только с пробелами удаляются, новая строка и отступ между и " This line will be output indented two spaces."
сохраняет свои ведущие пробелы (но я думаю, что это также зависит от настроек для полосы/сохранения/нормализации). "&N; " в конце строки вставляет новую строку, но также гарантирует, что любые следующие пробелы игнорируются, поскольку они отображаются между двумя узлами.
проблема у меня есть, когда я хочу вывести отступ, который начинается с
это уродливый Хак, я уверен, но, по крайней мере, у меня нет многословия "
Edit: В ответ на комментарии, вот как это выглядит без "макросов":
<xsl:template match="/">
<xsl:text>Flush left, despite the indentation.</xsl:text>
<xsl:text> This line will be output indented two spaces.</xsl:text>
<xsl:for-each select="//foo">
<xsl:text> Starts with two blanks: </xsl:text><xsl:value-of select="@bar"/>.<xsl:text>
</xsl:text>
<xsl:text> </xsl:text><xsl:value-of select="@abc"/><xsl:text> </xsl:text><xsl:value-of select="@xyz"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
Я думаю, что это делает его менее ясным, чтобы увидеть предполагаемый выходной отступ, и он закручивает отступ самого XSL потому что </xsl:text>
конечные теги должны отображаться в столбце 1 файла XSL (в противном случае вы получите нежелательные пробелы в выходном файле).
Что касается редактирования новых строк, вы можете использовать этот шаблон для рекурсивной замены одной строки в другой строке, и вы можете использовать его для разрывов строк:
<xsl:template name="replace.string.section">
<xsl:param name="in.string"/>
<xsl:param name="in.characters"/>
<xsl:param name="out.characters"/>
<xsl:choose>
<xsl:when test="contains($in.string,$in.characters)">
<xsl:value-of select="concat(substring-before($in.string,$in.characters),$out.characters)"/>
<xsl:call-template name="replace.string.section">
<xsl:with-param name="in.string" select="substring-after($in.string,$in.characters)"/>
<xsl:with-param name="in.characters" select="$in.characters"/>
<xsl:with-param name="out.characters" select="$out.characters"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$in.string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
вызовите его следующим образом (этот пример заменяет разрывы строк в $some.строковая переменная с пробелом):
<xsl:call-template name="replace.string.section">
<xsl:with-param name="in.string" select="$some.string"/>
<xsl:with-param name="in.characters" select="'
'"/>
<xsl:with-param name="out.characters" select="' '"/>
</xsl:call-template>