Подтвердить что ты не робот

Преобразование XML в обычный текст - как я должен игнорировать/обрабатывать пробелы в XSLT?

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

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

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

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

4b9b3361

Ответ 1

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

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

Атрибут elements в <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>

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

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

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

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

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

Ответ 2

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

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

Также, если вы используете xsl: value-of, вы можете использовать disable-output-escaping = "yes", чтобы помочь с некоторыми проблемами с пробелами.

Ответ 3

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

( "s" для пробела, "e" для пустого, "n" для новой строки.)

<?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' работает до текста node, содержащего хотя бы один символ без пробелов, потому что он расширяется до этого:

<xsl:template match="/">
  <xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text>
</xsl:text>

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

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

Это безобразный взлом, я уверен, но, по крайней мере, у меня нет подробного "< xsl: text > " теги, засоряющие мой 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> end должны появляться в столбце 1 файла XSL (в противном случае вы получите нежелательные пробелы в выходном файле).

Ответ 4

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

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