2008-10-08 1 views
35

Я пытаюсь преобразовать XML-файл в разметку, используемую dokuwiki, используя XSLT. Это действительно работает в некоторой степени, но отступы в XSL-файле вставляются в результаты. На данный момент у меня есть два варианта: полностью отказаться от этой вещи XSLT и найти другой способ преобразования из XML в разметку dokuwiki или удалить около 95% пробелов из XSL-файла, что делает его почти нечитаемым и кошмаром обслуживания.Преобразование XML в обычный текст - как я должен игнорировать/обрабатывать пробелы в XSLT?

Есть ли способ сохранить отступ в файле XSL без передачи всего этого пробела в окончательный документ?

Справочная информация: Я переношу средство autodoc со статических HTML-страниц на dokuwiki, поэтому API, разработанный командой сервера, может быть дополнительно документирован командой приложений, когда команда приложений работает с плохо документированным кодом. Логика состоит в том, чтобы раздел каждой страницы был отложен для инструмента autodoc и позволял комментировать где-либо вне этого блока. Я использую XSLT, потому что у нас уже есть XSL-файл для преобразования из XML в XHTML, и я предполагаю, что быстрее переписать XSL, чем свернуть свое собственное решение с нуля.

Редактировать: Ах, правильно, глупо меня, я пренебрег атрибутом отступа. (Другое примечание: я новичок в XSLT.) С другой стороны, мне все еще приходится иметь дело с новыми линиями. Dokuwiki использует каналы для различения столбцов таблицы, что означает, что все данные в строке таблицы должны быть в одной строке. Есть ли способ подавить вывод строк (иногда изредка), поэтому я могу сделать довольно сложную логику для каждой ячейки таблицы в некотором читабельном fasion?

ответ

75

Есть три причины для получения нежелательных пробелов в результате преобразований XSLT:

  1. пробельные, что происходит от между узлами в исходном документе
  2. пропуски, который приходит изнутри узлами в исходном документе
  3. пропуски, который приходит из таблицы стилей

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

Для устранения пробелов, которое между узлами в исходном документе, вы должны использовать <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> элементов, которые является значительным (иначе вы бы получили «Это ** важная *** точка *»). Для этого используйте

<xsl:strip-space elements="*" /> 
<xsl:preserve-space elements="li" /> 

Атрибут на <xsl:preserve-space>elements должен перечислять все элементы в документе, которые смешались содержание.

Помимо: использование <xsl:strip-space> также уменьшает размер исходного дерева в памяти, и делает таблицы стилей более эффективными, так что это стоит делать, даже если у вас нет пробелов проблем подобного рода.

Чтобы устранить пробелы, которые появляются в узлах исходного документа, вы должны использовать normalize-space(). Например, если у вас есть:

<dt> 
    a definition 
</dt> 

, и вы можете быть уверены, что <dt> элемент не будет проводить какие-либо элементы, которые вы хотите сделать что-то с, то вы можете сделать:

<xsl:template match="dt"> 
    ... 
    <xsl:value-of select="normalize-space(.)" /> 
    ... 
</xsl:template> 

ведущее и завершающее пробелы будут удалены из значения элемента <dt>, и вы просто получите строку "a definition".

Для устранения пробелов, поступающих из таблицы стилей, которая, возможно, один вы испытываете, когда у вас есть текст в шаблоне:

<xsl:template match="name"> 
    Name: 
    <xsl:value-of select="." /> 
</xsl:template> 

XSLT таблица стили разобранных таким же образом, как исходные документы, которые они обрабатывают, поэтому приведенный выше XSLT интерпретируется как дерево, содержащее элемент <xsl:template> с атрибутом match, чей первый дочерний элемент является текстовым узлом, а второй дочерний элемент - <xsl:value-of> с атрибутом select. Текстовый узел имеет ведущие и конечные пробелы (включая разрывы строк); так как это буквальный текст в таблице стилей, он буквально копируется в результат со всеми ведущими и конечными пробелами.

Но пробелы в таблицах стилей 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>.

+0

Это было очень полезно! Благодарю. – Black 2008-12-17 11:59:42

+0

Это действительно помогло, но я озадачен вашим использованием фразы «между узлами». Не правда ли, что все пробелы содержатся в текстовых узлах? Что вы подразумеваете под «между узлами»? Если бы я не узнал ваше имя, я бы предположил, что вам нужна лекция о структуре документа XML. – LarsH 2010-09-05 01:58:08

4

Вы используете indent = "no" в своем выходном теге?

<xsl:output method="text" indent="no" /> 

Кроме того, если вы используете XSL: стоимость вас может использовать отключаем-выход-вытекание = «да», чтобы помочь с некоторыми вопросами пробельных.

+4

В большинстве случаев использование `disable-output-escaping` является неправильным способом выполнения действий. Это только для очень ограниченных ситуаций. Пропагандирование d-o-e таким образом, чтобы кто-то, кто не знает лучше, вероятно, более вреден, чем полезно. См. Http://www.dpawson.co.uk/xsl/sect2/N2215.html#d3702e223 – LarsH 2010-09-05 01:51:11

0

Что касается правку о новых линий, вы можете использовать этот шаблон для рекурсивного заменить одну строку в другую строку, и вы можете использовать его для разрывов строк:

<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.string с пробелом):

<xsl:call-template name="replace.string.section"> 
     <xsl:with-param name="in.string" select="$some.string"/> 
     <xsl:with-param name="in.characters" select="'&#xA;'"/> 
     <xsl:with-param name="out.characters" select="' '"/> 
    </xsl:call-template> 
3

@ Ответ JeniT замечательный, я просто хочу указать на трюк для управления пробелами. Я не уверен, что это лучший способ (или даже хороший способ), но он работает для меня пока.

(. "S" для пространства, "е" для пустой, "н" для перевода строки)

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

rules for stripping whitespace Поскольку говорят, что пробельные только текстовые узлы получить раздели, символ новой строки и отступы между < XSL: шаблон > и < XSL: текст > получить раздели (хорошо). Поскольку правила говорят, что текстовый узел с хотя бы одним символом пробела сохраняется, неявный текстовый узел, содержащий " This line will be output indented two spaces.", сохраняет свои ведущие пробелы (но я думаю, это также зависит от настроек для strip/preserve/normalize). «& n;» в конце строки вставляет новую строку, но также гарантирует, что любые последующие пробелы игнорируются, потому что они появляются между двумя узлами.

Проблема у меня есть, когда я хочу вывести отступы, начинающиеся с < xsl: значение >. В этом случае «& e;» не поможет, потому что прорезь отступов не привязана к любым символам без пробелов. Поэтому для этих случаев я использую «& s2;» или «& s4;», в зависимости от того, сколько отступов я хочу.

Это некрасив хак я уверен, но, по крайней мере, я не имею многословный «< XSL: текст >» теги замусоривание XSLT, и по крайней мере, я могу еще отступ самого XSLT поэтому разборчивые. Я чувствую, что я злоупотребляю XSLT тем, для чего он не предназначен (для обработки текста), и это лучшее, что я могу сделать.


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 (в противном случае вы получите нежелательные пробелы в выходном файле).

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