虽然这是一个相当古老的问题,但答案可能还有另一个尚未触及的角度。
Result
TL; DR 重要的是它的味道Transformer
是什么。(如果您通过未编写/无法更改的 Java 代码使用 xalan,这可能不是您想听到的。)
对于此答案中的演示,我将使用 PostgreSQL PL/Java,因为它带有一组示例函数,包括preparexmltransform
和transformxml
使用 Java 的基于 xalan 的 XSLT 1.0 的东西,并且有一些额外的参数用于测试目的。这里有一个重要的行为效应,如果没有这些额外的参数,我就不会看到。
我将从准备一个名为的转换开始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
,但缩进发生在任何一种方式)。
我还没有说那些额外的论点是做什么的(现在有两个,“howin”和“howout”!),但这即将到来,因为看看会发生什么改变,除了“howout”从 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 转换 API 说您必须能够处理Transformer
几乎任何风格的Source
. Result
,并让它做正确的事情。
所以这就是为什么那些 PL/Java 示例函数有“how”参数的原因:需要有一种方法来测试可以将相同 XML 内容传递给的所有必需方式以及可以返回 's 结果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>
嗯,这就是模式。对于Transformer
实际上必须直接生成字符或字节的序列化流的所有 API ,它会根据要求添加缩进。
当它被赋予SAXResult
, StAXResult
, 或DOMResult
写入时,它不会添加缩进,因为这些都是结构化的 XML API;就好像 xalan 将缩进严格视为一个序列化问题,从技术上讲,它在生成 SAX、StAX 或 DOM 时并没有序列化。
(上表还显示,当其他 API 执行时,StAX API 并不总是将空元素呈现为自关闭。附带问题,但很有趣。)
因此,如果您发现自己试图让 xalan 转换来进行缩进,但事实并非如此,请仔细检查Result
您要求Transformer
生成哪种形式。
编辑:最后一点:如果您直接在 Java 中进行编码,那么实际上根本不需要编写那些七行 XSLT 来获得无非是具有indent
输出属性集的身份转换。
如果你调用 no-argument 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
是 a StreamResult
,以便转换器进行序列化。