Как сделать отступ при преобразовании xsl на выходе?

Я использую xalan со следующим заголовком xsl:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"
    xmlns:redirect="http://xml.apache.org/xalan/redirect"
    extension-element-prefixes="redirect"
    xmlns:xalan="http://xml.apache.org/xalan">
<xsl:output method="text" indent="yes" xalan:indent-amount="4"/>

И вывод не имеет отступа.

У кого есть идеи?


person Vincent    schedule 08.03.2010    source источник
comment
Я использовал инструмент xsl в notepad++. Не удалось сделать отступ вывода, когда у меня была опечатка в моем xsl. Убедитесь, что ваш файл xsl имеет правильный синтаксис.   -  person flobadob    schedule 09.10.2013


Ответы (5)


Для отступа необходимо использовать другое пространство имен: http://xml.apache.org/xslt (см. эту проблему)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:redirect="http://xml.apache.org/xalan/redirect"
extension-element-prefixes="redirect"
xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output method="xml" indent="yes" xalan:indent-amount="4"/>
person Jirka    schedule 26.03.2011
comment
Пространство имен xalan задокументировано в xalan .apache.org/xalan-j/apidocs/org/apache/xml/serializer/ - person TWiStErRob; 08.08.2016
comment
http://xml.apache.org/xslt устарела даже в старой версии (см. Объявление пространства имен xalan ), используйте вместо этого http://xml.apache.org/xalan. - person TWiStErRob; 08.08.2016

Какое-то время боролся с этим, но случайно заработал:

ключ был в том, чтобы добавить <xsl:strip-space elements="*"/>

поэтому это будет выглядеть так:

<xsl:stylesheet 
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:java="http://xml.apache.org/xalan/java"
    xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output method="xml" encoding="ASCII" indent="yes" xalan:indent-amount="4"/>
<xsl:strip-space elements="*"/>

Не уверен, почему, но, вероятно, удаление всех пробелов помогает xalan выяснить отступ

person tevch    schedule 18.04.2013
comment
Без xsl:strip-space[@elements="*"] xsl пытается сохранить узлы пробелов из ввода в вывод. - person Timothy; 09.12.2016

Jirka-x1, спасибо за вопрос-ссылку. Я использовал следующее (предложено Эдом Кноллом 13 августа 2004 г.):

<xsl:stylesheet ... xmlns:xslt="http://xml.apache.org/xslt">
<xsl:output ... indent="yes" xslt:indent-amount="4" />

У меня это работает с xalan (java) 2.7.1.

person Eugen Labun    schedule 30.05.2011

Я думаю, вам нужно установить method на xml. Если это не сработает, попробуйте следующее:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan">

<xsl:output method="xml" encoding="UTF-8" indent="yes" xalan:indent-amount="4"/>
person thelost    schedule 08.03.2010
comment
Возможно ли, что вы просматриваете xml с помощью приложения, которое неправильно отображает содержимое? - person thelost; 09.03.2010

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

TL; DR имеет значение, в какую разновидность Result подается Transformer. (Если вы используете xalan через Java-код, который вы не написали/не можете изменить, это может быть не то, что вам нужно слышать.)

Для демонстрации в этом ответе я буду использовать PostgreSQL PL/Java, поскольку он поставляется с набором примеров функций, включая preparexmltransform и transformxml, которые используют материал XSLT 1.0 на основе Java на основе xalan и имеют некоторые дополнительные аргументы для целей тестирования. Здесь есть важный поведенческий эффект, который я бы не увидел без этих дополнительных аргументов.

Я начну с подготовки преобразования с именем indent:

SELECT
 preparexmltransform(
  'indent',
  '<xsl:transform version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
     <xsl:copy-of select="."/>
    </xsl:template>
   </xsl:transform>',
  how => 5);

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

Так или иначе, давайте применим это преобразование к некоторому XML и посмотрим, что произойдет:

SELECT
  transformxml(
   'indent',
   '<a b="c" d="e"><f><g/><h/></f></a>',
   howin => 5, howout => 4);

  transformxml
----------------
<a b="c" d="e">
    <f>
        <g/>
        <h/>
    </f>
</a>

Круто, это сразу сделало то, что нужно, и показывает, что короткого преобразования выше достаточно; в частности, ему не нужно свойство xalan:indent-amount (если только вам не нравится другая ширина отступа), поэтому ему не нужно определять пространство имен xalan, и для его работы не обязательно должен быть элемент strip-space (если вы попробуете с пробелами во входном документе к ним просто добавляются пробелы отступа, что может выглядеть глупо, поэтому вы можете выбрать использование strip-space, но отступ происходит в любом случае).

Я еще не сказал, что делают эти дополнительные аргументы (теперь их два, как и как!), но это будет, потому что смотрите, что происходит, ничего не меняя, кроме как с 4 на 5:

SELECT
  transformxml(
   'indent',
   '<a b="c" d="e"><f><g/><h/></f></a>',
   howin => 5, howout => 5);

            transformxml            
------------------------------------
 <a b="c" d="e"><f><g/><h/></f></a>

Таким образом, Howout имеет значение для того, происходит ли отступ. Что это за хаки?

Что ж, у Java не один API для работы с XML. У него есть несколько, включая DOM, StAX и SAX, не говоря уже о том, что вы можете просто захотеть обрабатывать XML как String, или поток символов через Reader/Writer, или закодированный поток байтов через InputStream/OutputStream.

Спецификация JDBC говорит, что если вы пишете код Java для работы с XML в базе данных, SQLXML API должен предоставить вам на выбор любой из этих способов работы с данными, который удобен для вашей задачи. И JAXP Transformations API говорит, что вы должны быть в состоянии передать Transformer почти любой вариант Source и любой вариант Result, и заставить его делать правильные вещи.

Вот почему эти примеры функций PL/Java имеют аргументы «как»: должен быть способ протестировать все необходимые способы передачи одного и того же XML-контента в Transformer и все способы возврата результата Transformer. Хау устроены (произвольно) так:

 code |        form         |    howin     |   howout
------+---------------------+--------------+--------------
   1  | binary stream       | InputStream  | OutputStream
   2  | character stream    | Reader       | Writer
   3  | String              | String       | String
   4  | binary or character | StreamSource | StreamResult
   5  | SAX                 | SAXSource    | SAXResult
   6  | StAX                | StAXSource   | StAXResult
   7  | DOM                 | DOMSource    | DOMResult

Так что же делает одно и то же преобразование отступа xalan, когда оно вызывается с разными способами получения результата?

SELECT
  i, transformxml(
   'indent',
   '<a b="c" d="e"><f><g/><h/></f></a>',
   howin => 5, howout => i)
  FROM
   generate_series(1,7) AS i;

 i |               transformxml
---+------------------------------------------
 1 | <a b="c" d="e">
   |     <f>
   |         <g/>
   |         <h/>
   |     </f>
   | </a>
   |
 2 | <a b="c" d="e">
   |     <f>
   |         <g/>
   |         <h/>
   |     </f>
   | </a>
   |
 3 | <a b="c" d="e">
   |     <f>
   |         <g/>
   |         <h/>
   |     </f>
   | </a>
   |
 4 | <a b="c" d="e">
   |     <f>
   |         <g/>
   |         <h/>
   |     </f>
   | </a>
   |
 5 | <a b="c" d="e"><f><g/><h/></f></a>
 6 | <a b="c" d="e"><f><g></g><h></h></f></a>
 7 | <a b="c" d="e"><f><g/><h/></f></a>

Ну вот и шаблон. Для всех API, где Transformer фактически должен непосредственно создавать сериализованный поток символов или байтов, он добавляет отступ по запросу.

Когда ему дается SAXResult, StAXResult или DOMResult для записи, он не добавляет отступ, потому что все это структурные XML API; это как если бы xalan рассматривал отступы исключительно как проблему сериализации и технически не сериализует, когда создает SAX, StAX или DOM.

(В приведенной выше таблице также показано, что StAX API не всегда отображает пустой элемент как самозакрывающийся, когда это делают другие API. Дополнительный вопрос, но интересный.)

Итак, если вы обнаружите, что пытаетесь заставить xalan-преобразование делать отступы, а это не так, дважды проверьте, какую форму Result вы просите Transformer создать.

Редактировать: И последнее замечание: если вы кодируете это непосредственно на Java, на самом деле нет никакой необходимости писать эти семь строк XSLT только для того, чтобы получить то, что представляет собой не что иное, как удостоверение личности. Преобразование с установленным выходным свойством indent.

Если вы вызываете без аргументов TransformerFactory.newTransformer(), это прямо дает простое преобразование идентичности. Затем все, что вам нужно сделать, это установить его выходные свойства, и вы в деле:

var tf = javax.xml.transform.TransformerFactory.newInstance();
var t = tf.newTransformer();
t.setOutputProperty("indent", "yes");
t.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "1"); // if you don't like the default 4
t.transform(source, result);

Не бывает намного проще. Опять же, очень важно, чтобы result было StreamResult, чтобы преобразователь выполнял сериализацию.

person Chapman Flack    schedule 10.03.2020