我有一个包含XML的Java字符串,没有换行或缩进。我想把它变成一个字符串与格式良好的XML。我怎么做呢?
String unformattedXml = "<tag><nested>hello</nested></tag>";
String formattedXml = new [UnknownClass]().format(unformattedXml);
注意:我的输入是一个字符串。输出是一个字符串。
(基本)模拟结果:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
<nested>hello</nested>
</tag>
</root>
除了max、codeskrap、David Easley和milosmns给出的答案外,还可以看看我的轻量级、高性能漂亮打印机库:xml-formatter
// construct lightweight, threadsafe, instance
PrettyPrinter prettyPrinter = PrettyPrinterBuilder.newPrettyPrinter().build();
StringBuilder buffer = new StringBuilder();
String xml = ..; // also works with char[] or Reader
if(prettyPrinter.process(xml, buffer)) {
// valid XML, print buffer
} else {
// invalid XML, print xml
}
有时,就像直接从文件运行模拟SOAP服务时,有一个漂亮的打印机也能处理已经打印好的XML是很好的:
PrettyPrinter prettyPrinter = PrettyPrinterBuilder.newPrettyPrinter().ignoreWhitespace().build();
正如一些人评论的那样,漂亮打印只是一种以更适合人类阅读的形式表示XML的方法——严格来说,XML数据中不应该有空格。
该库用于日志记录的漂亮打印,还包括用于过滤(子树移除/匿名化)和漂亮打印CDATA和Text节点中的XML的函数。
I have found that in Java 1.6.0_32 the normal method to pretty print an XML string (using a Transformer with a null or identity xslt) does not behave as I would like if tags are merely separated by whitespace, as opposed to having no separating text. I tried using <xsl:strip-space elements="*"/> in my template to no avail. The simplest solution I found was to strip the space the way I wanted using a SAXSource and XML filter. Since my solution was for logging I also extended this to work with incomplete XML fragments. Note the normal method seems to work fine if you use a DOMSource but I did not want to use this because of the incompleteness and memory overhead.
public static class WhitespaceIgnoreFilter extends XMLFilterImpl
{
@Override
public void ignorableWhitespace(char[] arg0,
int arg1,
int arg2) throws SAXException
{
//Ignore it then...
}
@Override
public void characters( char[] ch,
int start,
int length) throws SAXException
{
if (!new String(ch, start, length).trim().equals(""))
super.characters(ch, start, length);
}
}
public static String prettyXML(String logMsg, boolean allowBadlyFormedFragments) throws SAXException, IOException, TransformerException
{
TransformerFactory transFactory = TransformerFactory.newInstance();
transFactory.setAttribute("indent-number", new Integer(2));
Transformer transformer = transFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
StringWriter out = new StringWriter();
XMLReader masterParser = SAXHelper.getSAXParser(true);
XMLFilter parser = new WhitespaceIgnoreFilter();
parser.setParent(masterParser);
if(allowBadlyFormedFragments)
{
transformer.setErrorListener(new ErrorListener()
{
@Override
public void warning(TransformerException exception) throws TransformerException
{
}
@Override
public void fatalError(TransformerException exception) throws TransformerException
{
}
@Override
public void error(TransformerException exception) throws TransformerException
{
}
});
}
try
{
transformer.transform(new SAXSource(parser, new InputSource(new StringReader(logMsg))), new StreamResult(out));
}
catch (TransformerException e)
{
if(e.getCause() != null && e.getCause() instanceof SAXParseException)
{
if(!allowBadlyFormedFragments || !"XML document structures must start and end within the same entity.".equals(e.getCause().getMessage()))
{
throw e;
}
}
else
{
throw e;
}
}
out.flush();
return out.toString();
}
现在已经是2012年了,Java可以比以前的XML做更多的事情,我想在我公认的答案之外添加一个替代方案。这在Java 6之外没有依赖关系。
import org.w3c.dom.Node;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
/**
* Pretty-prints xml, supplied as a string.
* <p/>
* eg.
* <code>
* String formattedXml = new XmlFormatter().format("<tag><nested>hello</nested></tag>");
* </code>
*/
public class XmlFormatter {
public String format(String xml) {
try {
final InputSource src = new InputSource(new StringReader(xml));
final Node document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src).getDocumentElement();
final Boolean keepDeclaration = Boolean.valueOf(xml.startsWith("<?xml"));
//May need this: System.setProperty(DOMImplementationRegistry.PROPERTY,"com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");
final DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
final DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
final LSSerializer writer = impl.createLSSerializer();
writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); // Set this to true if the output needs to be beautified.
writer.getDomConfig().setParameter("xml-declaration", keepDeclaration); // Set this to true if the declaration is needed to be outputted.
return writer.writeToString(document);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
String unformattedXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><QueryMessage\n" +
" xmlns=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message\"\n" +
" xmlns:query=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/query\">\n" +
" <Query>\n" +
" <query:CategorySchemeWhere>\n" +
" \t\t\t\t\t <query:AgencyID>ECB\n\n\n\n</query:AgencyID>\n" +
" </query:CategorySchemeWhere>\n" +
" </Query>\n\n\n\n\n" +
"</QueryMessage>";
System.out.println(new XmlFormatter().format(unformattedXml));
}
}
使用scala:
import xml._
val xml = XML.loadString("<tag><nested>hello</nested></tag>")
val formatted = new PrettyPrinter(150, 2).format(xml)
println(formatted)
如果你依赖scala-library.jar,你也可以在Java中这样做。它是这样的:
import scala.xml.*;
public class FormatXML {
public static void main(String[] args) {
String unformattedXml = "<tag><nested>hello</nested></tag>";
PrettyPrinter pp = new PrettyPrinter(150, 3);
String formatted = pp.format(XML.loadString(unformattedXml), TopScope$.MODULE$);
System.out.println(formatted);
}
}
PrettyPrinter对象是用两个整数构造的,第一个是最大行长,第二个是缩进步骤。