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