Преобразование 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:

  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> элементы, которые 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 правила зачистки пробелов скажем, что текстовые узлы только с пробелами удаляются, новая строка и отступ между

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

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


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

<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="'&#xA;'"/>
        <xsl:with-param name="out.characters" select="' '"/>
    </xsl:call-template>