Java에서 XML을 예쁘게 인쇄하는 방법
줄 바꿈이나 들여쓰기가 없는 XML을 포함하는 Java 문자열이 있습니다.포맷이 좋은 XML을 가진 문자열로 변환하고 싶은데 어떻게 해야 하나요?
String unformattedXml = "<tag><nested>hello</nested></tag>";
String formattedXml = new [UnknownClass]().format(unformattedXml);
주의: 입력은 String입니다.출력은 String 입니다.
(기본) 모의 결과:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
<nested>hello</nested>
</tag>
</root>
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
// initialize StreamResult with File object to save to file
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
String xmlString = result.getWriter().toString();
System.out.println(xmlString);
주의: Java 버전에 따라 결과가 다를 수 있습니다.사용하시는 플랫폼 고유의 회피책을 검색합니다.
여기 제 질문에 대한 답이 있습니다.다양한 결과의 답을 조합하여 XML을 예쁘게 프린트하는 클래스를 작성했습니다.
잘못된 XML 또는 큰 문서로 응답하는 방법에 대한 보장은 없습니다.
package ecb.sdw.pretty;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
/**
* 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 XmlFormatter() {
}
public String format(String unformattedXml) {
try {
final Document document = parseXmlFile(unformattedXml);
OutputFormat format = new OutputFormat(document);
format.setLineWidth(65);
format.setIndenting(true);
format.setIndent(2);
Writer out = new StringWriter();
XMLSerializer serializer = new XMLSerializer(out, format);
serializer.serialize(document);
return out.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Document parseXmlFile(String in) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(in));
return db.parse(is);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException 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));
}
}
다음과 같은 답변을 바탕으로 한 보다 심플한 솔루션을 제공합니다.
public static String prettyFormat(String input, int indent) {
try {
Source xmlInput = new StreamSource(new StringReader(input));
StringWriter stringWriter = new StringWriter();
StreamResult xmlOutput = new StreamResult(stringWriter);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute("indent-number", indent);
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(xmlInput, xmlOutput);
return xmlOutput.getWriter().toString();
} catch (Exception e) {
throw new RuntimeException(e); // simple exception handling, please review it
}
}
public static String prettyFormat(String input) {
return prettyFormat(input, 2);
}
테스트 케이스:
prettyFormat("<root><child>aaa</child><child/></root>");
반환:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<child>aaa</child>
<child/>
</root>
//무시:원래 편집에서는 코드의 클래스 이름에 s가 누락되어 있으면 됩니다.SO에서 6자 이상의 검증을 얻기 위해 6자 이상의 중복 문자를 추가했습니다.
지금은 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));
}
}
상위 등급의 답변에는 xerces를 사용해야 합니다.
이 외부 종속성을 추가하지 않으려면 표준 jdk 라이브러리(실제로 내부적으로 xerces를 사용하여 구축됨)를 사용하면 됩니다.
N.B. jdk 버전 1.5에서 버그가 발생했습니다. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446을 참조하십시오.
(오류가 발생하면 원래 텍스트가 반환됩니다.)
package com.test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.InputSource;
public class XmlTest {
public static void main(String[] args) {
XmlTest t = new XmlTest();
System.out.println(t.formatXml("<a><b><c/><d>text D</d><e value='0'/></b></a>"));
}
public String formatXml(String xml){
try{
Transformer serializer= SAXTransformerFactory.newInstance().newTransformer();
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
//serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
//serializer.setOutputProperty("{http://xml.customer.org/xslt}indent-amount", "2");
Source xmlSource=new SAXSource(new InputSource(new ByteArrayInputStream(xml.getBytes())));
StreamResult res = new StreamResult(new ByteArrayOutputStream());
serializer.transform(xmlSource, res);
return new String(((ByteArrayOutputStream)res.getOutputStream()).toByteArray());
}catch(Exception e){
//TODO log error
return xml;
}
}
}
과거에 org.dom4j.io을 사용하여 인쇄한 적이 있습니다.OutputFormat.createPrettyPrint() 메서드
public String prettyPrint(final String xml){
if (StringUtils.isBlank(xml)) {
throw new RuntimeException("xml was null or blank in prettyPrint()");
}
final StringWriter sw;
try {
final OutputFormat format = OutputFormat.createPrettyPrint();
final org.dom4j.Document document = DocumentHelper.parseText(xml);
sw = new StringWriter();
final XMLWriter writer = new XMLWriter(sw, format);
writer.write(document);
}
catch (Exception e) {
throw new RuntimeException("Error pretty printing xml:\n" + xml, e);
}
return sw.toString();
}
다음은 dom4j를 사용하여 수행하는 방법입니다.
Import:
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
코드:
String xml = "<your xml='here'/>";
Document doc = DocumentHelper.parseText(xml);
StringWriter sw = new StringWriter();
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter xw = new XMLWriter(sw, format);
xw.write(doc);
String result = sw.toString();
String
가 있습니다.DOM
오오:Node
를 를 참조해 주세요.Transformer
다만, XML 문자열이 유효하고, 문자열을 DOM 에 해석해, DOM 를 개입시켜 변환을 실행해 문자열을 되돌리는 메모리 오버헤드를 일으키지 않는 경우는, 구식 문자를 해석하는 것만으로 끝납니다. 이 매 행 됩니다.</...>
카운터(스페이스의 하기 위해)는, 「」, 「」(스페이스의 수) 마다 증가합니다.<...>
마다 감소합니다.</...>
면책사항 - 아래 함수의 잘라내기/붙여넣기/텍스트 편집을 했으므로 그대로 컴파일할 수 없습니다.
public static final Element createDOM(String strXML)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true);
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource sourceXML = new InputSource(new StringReader(strXML));
Document xmlDoc = db.parse(sourceXML);
Element e = xmlDoc.getDocumentElement();
e.normalize();
return e;
}
public static final void prettyPrint(Node xml, OutputStream out)
throws TransformerConfigurationException, TransformerFactoryConfigurationError, TransformerException {
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.transform(new DOMSource(xml), new StreamResult(out));
}
Kevin Hakanson은 다음과 같이 말했습니다.「하지만, XML 문자열이 유효한 것을 알고, 문자열을 DOM 에 해석해, DOM 를 개입시켜 변환을 실행해 문자열을 되돌리는 메모리 오버헤드를 일으키고 싶지 않은 경우는, 문자 해석에 의해서 낡은 문자를 실행할 수 있습니다.각 문자 뒤에 줄 바꿈과 공백을 삽입하고, 모든 <...>에 대해 증가시키는 (스페이스 수를 결정하기 위해) 카운터를 유지 및 들여씁니다.> 및 표시되는 모든 값마다 감소합니다."
동감입니다. 이러한 접근은 훨씬 빠르고 의존성이 훨씬 적습니다.
솔루션 예시:
/**
* XML utils, including formatting.
*/
public class XmlUtils
{
private static XmlFormatter formatter = new XmlFormatter(2, 80);
public static String formatXml(String s)
{
return formatter.format(s, 0);
}
public static String formatXml(String s, int initialIndent)
{
return formatter.format(s, initialIndent);
}
private static class XmlFormatter
{
private int indentNumChars;
private int lineLength;
private boolean singleLine;
public XmlFormatter(int indentNumChars, int lineLength)
{
this.indentNumChars = indentNumChars;
this.lineLength = lineLength;
}
public synchronized String format(String s, int initialIndent)
{
int indent = initialIndent;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++)
{
char currentChar = s.charAt(i);
if (currentChar == '<')
{
char nextChar = s.charAt(i + 1);
if (nextChar == '/')
indent -= indentNumChars;
if (!singleLine) // Don't indent before closing element if we're creating opening and closing elements on a single line.
sb.append(buildWhitespace(indent));
if (nextChar != '?' && nextChar != '!' && nextChar != '/')
indent += indentNumChars;
singleLine = false; // Reset flag.
}
sb.append(currentChar);
if (currentChar == '>')
{
if (s.charAt(i - 1) == '/')
{
indent -= indentNumChars;
sb.append("\n");
}
else
{
int nextStartElementPos = s.indexOf('<', i);
if (nextStartElementPos > i + 1)
{
String textBetweenElements = s.substring(i + 1, nextStartElementPos);
// If the space between elements is solely newlines, let them through to preserve additional newlines in source document.
if (textBetweenElements.replaceAll("\n", "").length() == 0)
{
sb.append(textBetweenElements + "\n");
}
// Put tags and text on a single line if the text is short.
else if (textBetweenElements.length() <= lineLength * 0.5)
{
sb.append(textBetweenElements);
singleLine = true;
}
// For larger amounts of text, wrap lines to a maximum line length.
else
{
sb.append("\n" + lineWrap(textBetweenElements, lineLength, indent, null) + "\n");
}
i = nextStartElementPos - 1;
}
else
{
sb.append("\n");
}
}
}
}
return sb.toString();
}
}
private static String buildWhitespace(int numChars)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < numChars; i++)
sb.append(" ");
return sb.toString();
}
/**
* Wraps the supplied text to the specified line length.
* @lineLength the maximum length of each line in the returned string (not including indent if specified).
* @indent optional number of whitespace characters to prepend to each line before the text.
* @linePrefix optional string to append to the indent (before the text).
* @returns the supplied text wrapped so that no line exceeds the specified line length + indent, optionally with
* indent and prefix applied to each line.
*/
private static String lineWrap(String s, int lineLength, Integer indent, String linePrefix)
{
if (s == null)
return null;
StringBuilder sb = new StringBuilder();
int lineStartPos = 0;
int lineEndPos;
boolean firstLine = true;
while(lineStartPos < s.length())
{
if (!firstLine)
sb.append("\n");
else
firstLine = false;
if (lineStartPos + lineLength > s.length())
lineEndPos = s.length() - 1;
else
{
lineEndPos = lineStartPos + lineLength - 1;
while (lineEndPos > lineStartPos && (s.charAt(lineEndPos) != ' ' && s.charAt(lineEndPos) != '\t'))
lineEndPos--;
}
sb.append(buildWhitespace(indent));
if (linePrefix != null)
sb.append(linePrefix);
sb.append(s.substring(lineStartPos, lineEndPos + 1));
lineStartPos = lineEndPos + 1;
}
return sb.toString();
}
// other utils removed for brevity
}
서드파티제 XML 라이브러리를 사용해도 문제가 없다면 현재 가장 높은 투표율을 보이고 있는 답변보다 훨씬 간단한 방법으로 문제를 해결할 수 있습니다.
입력과 출력이 모두 Strings여야 한다고 명시되어 있습니다.그러므로 XOM 라이브러리에서 구현되는 유틸리티 메서드는 다음과 같습니다.
import nu.xom.*;
import java.io.*;
[...]
public static String format(String xml) throws ParsingException, IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Serializer serializer = new Serializer(out);
serializer.setIndent(4); // or whatever you like
serializer.write(new Builder().build(xml, ""));
return out.toString("UTF-8");
}
동작하는 것을 테스트해 본 결과, JRE 버전 등에 따라 결과는 달라지지 않습니다.출력 포맷을 원하는 대로 커스터마이즈하는 방법은 API를 참조하십시오.
요. 줄이 더 Serializer
OutputStream
아아아아아아아아아아아아아아아아아아아아.그러나 실제 XML을 빈둥거릴 수 있는 코드는 거의 없습니다.
(이 답변은 XOM에 대한 저의 평가의 일부입니다.이 답변은 dom4j를 대체할 수 있는 최적의 Java XML 라이브러리에 대한 질문의 한 가지 옵션으로 제안되었습니다.참고로, dom4j를 사용하면, 및 를 사용해 간단하게 이것을 실현할 수 있습니다.편집: ...는 mlo55의 답변에 나타나 있습니다.)
음... 이런 걸 겪었는데 잘 알려진 벌레야...이 Output Property 를 추가합니다.
transformer.setOutputProperty(OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "8");
이게 도움이 됐으면 좋겠는데...
「먼저 DOM 트리를 구축하지 않으면 안 된다」라고 하는 코멘트에 대해서는, 아니요, 그럴 필요도 없고, 그렇게 해서는 안 됩니다.
대신 StreamSource(new StreamSource(new StringReader(str)))를 생성하여 해당 ID 트랜스포머에 공급합니다.그러면 SAX 파서를 사용할 수 있고, 결과도 훨씬 빨라집니다.이 경우 중간 트리를 만드는 것은 순전히 오버헤드입니다.그렇지 않으면 1위 답변이 좋습니다.
스칼라 사용:
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
오브젝트는 2개의 int로 구성됩니다.첫 번째 int는 최대 행 길이이고 두 번째 integration 스텝입니다.
milosmns에서 약간 개선된 버전...
public static String getPrettyXml(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("<?")) {
pretty.append(row + "\n");
} else if (row.startsWith("</")) {
String indent = repeatString(--stack);
pretty.append(indent + row + "\n");
} else if (row.startsWith("<") && row.endsWith("/>") == false) {
String indent = repeatString(stack++);
pretty.append(indent + row + "\n");
if (row.endsWith("]]>")) stack--;
} else {
String indent = repeatString(stack);
pretty.append(indent + row + "\n");
}
}
return pretty.toString().trim();
}
private static String repeatString(int stack) {
StringBuilder indent = new StringBuilder();
for (int i = 0; i < stack; i++) {
indent.append(" ");
}
return indent.toString();
}
나중에 참고할 수 있도록 다음과 같은 해결책이 있습니다(@George Hawkins가 답변 중 하나에 올린 코멘트 덕분에).
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
LSSerializer writer = impl.createLSSerializer();
writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
LSOutput output = impl.createLSOutput();
ByteArrayOutputStream out = new ByteArrayOutputStream();
output.setByteStream(out);
writer.write(document, output);
String xmlStr = new String(out.toByteArray());
위의 모든 솔루션이 작동하지 않다가 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();
}
아래 코드는 완벽하게 작동합니다.
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
String formattedXml1 = prettyFormat("<root><child>aaa</child><child/></root>");
public static String prettyFormat(String input) {
return prettyFormat(input, "2");
}
public static String prettyFormat(String input, String indent) {
Source xmlInput = new StreamSource(new StringReader(input));
StringWriter stringWriter = new StringWriter();
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", indent);
transformer.transform(xmlInput, new StreamResult(stringWriter));
String pretty = stringWriter.toString();
pretty = pretty.replace("\r\n", "\n");
return pretty;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
나는 그것들을 모두 섞어서 작은 프로그램 하나를 쓴다.xml 파일을 읽고 출력하고 있습니다.xzy 대신 파일 경로를 지정하십시오.
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new FileInputStream(new File("C:/Users/xyz.xml")));
prettyPrint(doc);
}
private static String prettyPrint(Document document)
throws TransformerException {
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
DOMSource source = new DOMSource(document);
StringWriter strWriter = new StringWriter();
StreamResult result = new StreamResult(strWriter);transformer.transform(source, result);
System.out.println(strWriter.getBuffer().toString());
return strWriter.getBuffer().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();
}
우리에게 도움이 되는 또 하나의 솔루션
import java.io.StringWriter;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
**
* Pretty Print XML String
*
* @param inputXmlString
* @return
*/
public static String prettyPrintXml(String xml) {
final StringWriter sw;
try {
final OutputFormat format = OutputFormat.createPrettyPrint();
final org.dom4j.Document document = DocumentHelper.parseText(xml);
sw = new StringWriter();
final XMLWriter writer = new XMLWriter(sw, format);
writer.write(document);
}
catch (Exception e) {
throw new RuntimeException("Error pretty printing xml:\n" + xml, e);
}
return sw.toString();
}
jdom2 사용 : http://www.jdom.org/
import java.io.StringReader;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
String prettyXml = new XMLOutputter(Format.getPrettyFormat()).
outputString(new SAXBuilder().build(new StringReader(uglyXml)));
max의 답변 대신 codeskraps, 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도 처리하는 pretty-printer를 사용하는 것이 좋습니다.
PrettyPrinter prettyPrinter = PrettyPrinterBuilder.newPrettyPrinter().ignoreWhitespace().build();
일부에서 언급했듯이, 예쁜 인쇄는 XML을 사람이 읽기 쉬운 형태로 표시하는 방법일 뿐이며, 공백 공간은 XML 데이터에 완전히 속하지 않습니다.
라이브러리는 로깅을 위한 예쁜 인쇄를 목적으로 하며, CDATA 및 텍스트 노드에서 필터링(서브트리 제거/익명화) 및 XML 예쁜 인쇄 기능도 포함합니다.
저도 같은 문제가 있어서 JTidy(http://jtidy.sourceforge.net/index.html)에서 큰 성공을 거두고 있습니다.
예:
Tidy t = new Tidy();
t.setIndentContent(true);
Document d = t.parseDOM(
new ByteArrayInputStream("HTML goes here", null);
OutputStream out = new ByteArrayOutputStream();
t.pprint(d, out);
String html = out.toString();
XML이 100% 유효할 필요는 없습니다.예를 들어 REST/SOAP 로깅의 경우(다른 사용자가 무엇을 전송하는지 알 수 없습니다;-)
온라인에서 찾은 코드 스니핑이 유효한 접근법으로는 아직 여기에 없는 것으로 생각됩니다.
public static String prettyPrintXMLAsString(String xmlString) {
/* Remove new lines */
final String LINE_BREAK = "\n";
xmlString = xmlString.replaceAll(LINE_BREAK, "");
StringBuffer prettyPrintXml = new StringBuffer();
/* Group the xml tags */
Pattern pattern = Pattern.compile("(<[^/][^>]+>)?([^<]*)(</[^>]+>)?(<[^/][^>]+/>)?");
Matcher matcher = pattern.matcher(xmlString);
int tabCount = 0;
while (matcher.find()) {
String str1 = (null == matcher.group(1) || "null".equals(matcher.group())) ? "" : matcher.group(1);
String str2 = (null == matcher.group(2) || "null".equals(matcher.group())) ? "" : matcher.group(2);
String str3 = (null == matcher.group(3) || "null".equals(matcher.group())) ? "" : matcher.group(3);
String str4 = (null == matcher.group(4) || "null".equals(matcher.group())) ? "" : matcher.group(4);
if (matcher.group() != null && !matcher.group().trim().equals("")) {
printTabs(tabCount, prettyPrintXml);
if (!str1.equals("") && str3.equals("")) {
++tabCount;
}
if (str1.equals("") && !str3.equals("")) {
--tabCount;
prettyPrintXml.deleteCharAt(prettyPrintXml.length() - 1);
}
prettyPrintXml.append(str1);
prettyPrintXml.append(str2);
prettyPrintXml.append(str3);
if (!str4.equals("")) {
prettyPrintXml.append(LINE_BREAK);
printTabs(tabCount, prettyPrintXml);
prettyPrintXml.append(str4);
}
prettyPrintXml.append(LINE_BREAK);
}
}
return prettyPrintXml.toString();
}
private static void printTabs(int count, StringBuffer stringBuffer) {
for (int i = 0; i < count; i++) {
stringBuffer.append("\t");
}
}
public static void main(String[] args) {
String x = new String(
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><soap:Fault><faultcode>soap:Client</faultcode><faultstring>INVALID_MESSAGE</faultstring><detail><ns3:XcbSoapFault xmlns=\"\" xmlns:ns3=\"http://www.someapp.eu/xcb/types/xcb/v1\"><CauseCode>20007</CauseCode><CauseText>INVALID_MESSAGE</CauseText><DebugInfo>Problems creating SAAJ object model</DebugInfo></ns3:XcbSoapFault></detail></soap:Fault></soap:Body></soap:Envelope>");
System.out.println(prettyPrintXMLAsString(x));
}
출력은 다음과 같습니다.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>INVALID_MESSAGE</faultstring>
<detail>
<ns3:XcbSoapFault xmlns="" xmlns:ns3="http://www.someapp.eu/xcb/types/xcb/v1">
<CauseCode>20007</CauseCode>
<CauseText>INVALID_MESSAGE</CauseText>
<DebugInfo>Problems creating SAAJ object model</DebugInfo>
</ns3:XcbSoapFault>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
항상 다음 기능을 사용하고 있습니다.
public static String prettyPrintXml(String xmlStringToBeFormatted) {
String formattedXmlString = null;
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setValidating(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputSource inputSource = new InputSource(new StringReader(xmlStringToBeFormatted));
Document document = documentBuilder.parse(inputSource);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
StreamResult streamResult = new StreamResult(new StringWriter());
DOMSource dOMSource = new DOMSource(document);
transformer.transform(dOMSource, streamResult);
formattedXmlString = streamResult.getWriter().toString().trim();
} catch (Exception ex) {
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
System.err.println(sw.toString());
}
return formattedXmlString;
}
Java 1.6.0_32에서는 XML 문자열을 예쁘게 인쇄하는 일반적인 방법(늘 또는 아이덴티티 xslt와 함께 트랜스포머를 사용)은 태그가 구분되지 않고 공백으로만 구분되어 있으면 원하는 대로 작동하지 않습니다.나는 그것을 사용해봤어요.<xsl:strip-space elements="*"/>
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★제가 찾은 가장 간단한 해결책은 SAXSource와 XML 필터를 사용하여 원하는 방식으로 공간을 제거하는 것이었습니다.이 솔루션은 로깅을 위한 것이었기 때문에 불완전한 XML fragment로 작업하도록 확장했습니다.DOMSource를 사용하면 일반 방법은 정상적으로 동작하는 것 같지만 불완전성과 메모리 오버헤드 때문에 사용하지 않았습니다.
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();
}
여기에서 찾은 Java 1.6+용 솔루션은 코드가 이미 포맷된 경우 포맷되지 않습니다.나에게 효과가 있었던 것은(그리고 이미 포맷된 코드를 재포맷한 것) 다음과 같습니다.
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.w3c.dom.Element;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.io.StringReader;
public class XmlUtils {
public static String toCanonicalXml(String xml) throws InvalidCanonicalizerException, ParserConfigurationException, SAXException, CanonicalizationException, IOException {
Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
byte canonXmlBytes[] = canon.canonicalize(xml.getBytes());
return new String(canonXmlBytes);
}
public static String prettyFormat(String input) throws TransformerException, ParserConfigurationException, IOException, SAXException, InstantiationException, IllegalAccessException, ClassNotFoundException {
InputSource src = new InputSource(new StringReader(input));
Element document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src).getDocumentElement();
Boolean keepDeclaration = input.startsWith("<?xml");
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
LSSerializer writer = impl.createLSSerializer();
writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
writer.getDomConfig().setParameter("xml-declaration", keepDeclaration);
return writer.writeToString(document);
}
}
이 툴은 유닛테스트에서 풀스트링 xml 비교에 사용할 수 있습니다.
private void assertXMLEqual(String expected, String actual) throws ParserConfigurationException, IOException, SAXException, CanonicalizationException, InvalidCanonicalizerException, TransformerException, IllegalAccessException, ClassNotFoundException, InstantiationException {
String canonicalExpected = prettyFormat(toCanonicalXml(expected));
String canonicalActual = prettyFormat(toCanonicalXml(actual));
assertEquals(canonicalExpected, canonicalActual);
}
제가 본 답은Scala
또 요.Groovy
혹시 누가 재미있어 할까봐. 즉 2단계입니다.XmlNodePrinter
생성자에 다른 값을 전달할 수도 있습니다.
def xml = "<tag><nested>hello</nested></tag>"
def stringWriter = new StringWriter()
def node = new XmlParser().parseText(xml);
new XmlNodePrinter(new PrintWriter(stringWriter)).print(node)
println stringWriter.toString()
groovy jar가 클래스 경로에 있는 경우 Java에서 사용
String xml = "<tag><nested>hello</nested></tag>";
StringWriter stringWriter = new StringWriter();
Node node = new XmlParser().parseText(xml);
new XmlNodePrinter(new PrintWriter(stringWriter)).print(node);
System.out.println(stringWriter.toString());
줄 바꿈이 몇 개 필요 없는 경우에는 간단히 정규화하면 충분합니다.
String leastPrettifiedXml = uglyXml.replaceAll("><", ">\n<");
코드도 괜찮고, 흠집이 빠져서 나온 결과도 아닙니다.
(인디멘트가 있는 솔루션에 대해서는 다른 답변을 참조하십시오).
많은 사람들이 사용하는 많은 작업을 수행할 수 있는 xmlstarlet(http://xmlstar.sourceforge.net/)이라는 매우 좋은 명령줄 XML 유틸리티가 있습니다.
Runtime을 사용하여 이 프로그램을 프로그래밍 방식으로 실행할 수 있습니다.exec 및 포맷된 출력 파일을 읽습니다.Java 코드 몇 줄보다 더 많은 옵션과 더 나은 오류 보고 기능을 제공합니다.
xmlstarlet 다운로드 : http://sourceforge.net/project/showfiles.php?group_id=66612&package_id=64589
언급URL : https://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java
'IT이야기' 카테고리의 다른 글
GCC를 사용한 실행 파일에 리소스 내장 (0) | 2022.06.06 |
---|---|
Vuex 스토어에서 모듈 액세스 (0) | 2022.06.06 |
Windows(Visual C)용 unistd.h를 대체할 수 있는 것이 있습니까? (0) | 2022.06.06 |
동적 콘텐츠 높이를 고려한 vue 전환을 통한 원활한 확장/축소 전환 (0) | 2022.06.06 |
vuejs 웹 팩 관련 모듈을 찾을 수 없습니다. 오류 (0) | 2022.06.06 |