javax.xml.transform.Transformer 的漂亮打印输出,仅使用标准 java api(缩进和 Doctype 定位)
Posted
技术标签:
【中文标题】javax.xml.transform.Transformer 的漂亮打印输出,仅使用标准 java api(缩进和 Doctype 定位)【英文标题】:Pretty-printing output from javax.xml.transform.Transformer with only standard java api (Indentation and Doctype positioning) 【发布时间】:2010-11-18 21:18:58 【问题描述】:使用以下简单代码:
package test;
import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class TestOutputKeys
public static void main(String[] args) throws TransformerException
// Instantiate transformer input
Source xmlInput = new StreamSource(new StringReader(
"<!-- Document comment --><aaa><bbb/><ccc/></aaa>"));
StreamResult xmlOutput = new StreamResult(new StringWriter());
// Configure transformer
Transformer transformer = TransformerFactory.newInstance()
.newTransformer(); // An identity transformer
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "testing.dtd");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(xmlInput, xmlOutput);
System.out.println(xmlOutput.getWriter().toString());
我得到了输出:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Document comment --><!DOCTYPE aaa SYSTEM "testing.dtd">
<aaa>
<bbb/>
<ccc/>
</aaa>
问题 A:doctype 标签出现在文档注释之后。是否可以让它出现在文档注释之前?
问题 B:如何实现缩进,仅使用 JavaSE 5.0 API? 此问题与How to pretty-print xml from java 基本相同,然而该问题中的几乎所有答案都依赖于外部库。仅使用 java api 的唯一适用答案(由名为 Lorenzo Boccaccia 的用户发布)基本上等于上面发布的代码,但对我不起作用(如输出所示,我没有缩进)。
我猜你必须设置用于缩进的空格数量,正如外部库的许多答案所做的那样,但我只是找不到在 java api 中指定的位置。鉴于在 java api 中存在将缩进属性设置为“是”的可能性,因此必须能够以某种方式执行缩进。我就是想不通。
【问题讨论】:
问题 A 没有意义。您是指第二部分中的“之前”吗? 是的。我编辑了问题以更改拼写错误。谢谢。 重复我在***.com/questions/139076/… 中所做的评论 - 您现在可以在没有外部库的情况下进行漂亮的打印。见xerces.apache.org/xerces2-j/faq-general.html#faq-6。是的,这是一个 Xerces 常见问题解答,但答案涵盖了标准 JDK 类。这些类的最初 1.5 实现有很多问题,但从 1.6 开始一切正常。复制 FAQ 中的 LSSerializer 示例,去掉“...”位并在LSSerializer writer = ...
行之后添加 writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
。
此代码 sn-p 易受 XML 外部实体注入 (XXE) 的攻击。见cheatsheetseries.owasp.org/cheatsheets/…
【参考方案1】:
一个小工具类作为例子...
import org.apache.xml.serialize.XMLSerializer;
public class XmlUtil
public static Document file2Document(File file) throws Exception
if (file == null || !file.exists())
throw new IllegalArgumentException("File must exist![" + file == null ? "NULL"
: ("Could not be found: " + file.getAbsolutePath()) + "]");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
return dbFactory.newDocumentBuilder().parse(new FileInputStream(file));
public static Document string2Document(String xml) throws Exception
InputSource src = new InputSource(new StringReader(xml));
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
return dbFactory.newDocumentBuilder().parse(src);
public static OutputFormat getPrettyPrintFormat()
OutputFormat format = new OutputFormat();
format.setLineWidth(120);
format.setIndenting(true);
format.setIndent(2);
format.setEncoding("UTF-8");
return format;
public static String document2String(Document doc, OutputFormat format) throws Exception
StringWriter stringOut = new StringWriter();
XMLSerializer serial = new XMLSerializer(stringOut, format);
serial.serialize(doc);
return stringOut.toString();
public static String document2String(Document doc) throws Exception
return XmlUtil.document2String(doc, XmlUtil.getPrettyPrintFormat());
public static void document2File(Document doc, File file) throws Exception
XmlUtil.document2String(doc, XmlUtil.getPrettyPrintFormat());
public static void document2File(Document doc, File file, OutputFormat format) throws Exception
XMLSerializer serializer = new XMLSerializer(new FileOutputStream(file), format);
serializer.serialize(doc);
XMLserializer 由 Apache Foundation 中的 xercesImpl 提供。这是maven依赖:
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.11.0</version>
</dependency>
您可以在此处找到您最喜欢的构建工具的依赖项:http://mvnrepository.com/artifact/xerces/xercesImpl/2.11.0。
【讨论】:
请添加对外部库的引用。此示例仅适用于 JDK。 XMLSerializer 属于 org.apache.xml.serialize。【参考方案2】:要使输出成为有效的 XML 文档,NO。有效的 XML 文档必须以处理指令开头。有关详细信息,请参阅 XML 规范 http://www.w3.org/TR/REC-xml/#sec-prolog-dtd。
【讨论】:
这个答案是基于对问题的误解。注释可以在 doctype 声明之前或之后。 IE。您可以使用xmlDeclaration comment doctypeDeclaration
或xmlDeclaration doctypeDeclaration comment
。这个问题从未谈到在 xmlDeclaration 之前放置任何内容。【参考方案3】:
您可以使用XSLT file 美化所有内容。谷歌抛出了一些结果,但我无法评论它们的正确性。
【讨论】:
我喜欢这个主意。我在这类事情(命名空间操作、空白控制等)中使用了相当多的 XSLT。它效率不高,但很容易,而且不依赖于解析器。【参考方案4】:缺少的部分是缩进量。您可以设置缩进和缩进量如下:
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("http://xml.apache.org/xsltindent-amount", "2");
transformer.transform(xmlInput, xmlOutput);
【讨论】:
很高兴知道,我认为它失败了,因为我有一个旧版本的 xalan,仔细检查 此解决方案缩进生成的 XML 文档,编译时不会出现错误或警告。 正如你所说,它取决于 Xalan,但这是 jdk 的一部分。据我所知,没有 API 级别设置来设置缩进,所以如果用户使用不同的实现,您需要添加开关处理来设置该实现的缩进。但是您不能控制所使用的实现吗? 我对 api 是什么的看法似乎告诉我 api 应该由执行指定任务的函数/方法组成,并且在使用 api 时,不需要直接处理底层实现.但话又说回来,我只是一个新手程序员,也许事情只会按照我认为在乌托邦世界中应该有的方式工作。我仍然认为 OutputKeys.INDENT 存在于 api 级别的事实应该意味着 api 级别的缩进是可能的,除非 api 有缺陷(或者 Apache 的实现有缺陷,没有按照应有的方式解释属性) 这是我一直这样做的方式,但在这里它不起作用,可能是一个不同的 XML 库。我做了factory.setAttribute("indent-number", 4);
,现在可以了。以上是关于javax.xml.transform.Transformer 的漂亮打印输出,仅使用标准 java api(缩进和 Doctype 定位)的主要内容,如果未能解决你的问题,请参考以下文章