我有一个包含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>
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();
}
如果您确信您有一个有效的XML,那么这个很简单,并且避免了XML DOM树。可能有一些错误,如果你看到任何错误,请评论
public String prettyPrint(String xml) {
if (xml == null || xml.trim().length() == 0) return "";
int stack = 0;
StringBuilder pretty = new StringBuilder();
String[] rows = xml.trim().replaceAll(">", ">\n").replaceAll("<", "\n<").split("\n");
for (int i = 0; i < rows.length; i++) {
if (rows[i] == null || rows[i].trim().length() == 0) continue;
String row = rows[i].trim();
if (row.startsWith("<?")) {
// xml version tag
pretty.append(row + "\n");
} else if (row.startsWith("</")) {
// closing tag
String indent = repeatString(" ", --stack);
pretty.append(indent + row + "\n");
} else if (row.startsWith("<")) {
// starting tag
String indent = repeatString(" ", stack++);
pretty.append(indent + row + "\n");
} else {
// tag data
String indent = repeatString(" ", stack);
pretty.append(indent + row + "\n");
}
}
return pretty.toString().trim();
}
以上所有的解决方案都不适合我,然后我找到了这个http://myshittycode.com/2014/02/10/java-properly-indenting-xml-string/
线索就是用XPath删除空格
String xml = "<root>" +
"\n " +
"\n<name>Coco Puff</name>" +
"\n <total>10</total> </root>";
try {
Document document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8"))));
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']",
document,
XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i) {
Node node = nodeList.item(i);
node.getParentNode().removeChild(node);
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
StringWriter stringWriter = new StringWriter();
StreamResult streamResult = new StreamResult(stringWriter);
transformer.transform(new DOMSource(document), streamResult);
System.out.println(stringWriter.toString());
}
catch (Exception e) {
e.printStackTrace();
}