Làm thế nào để in XML đẹp từ Java?


442

Tôi có một Chuỗi Java chứa XML, không có nguồn cấp dữ liệu hoặc thụt dòng. Tôi muốn biến nó thành một Chuỗi với XML được định dạng độc đáo. Làm thế nào để tôi làm điều này?

String unformattedXml = "<tag><nested>hello</nested></tag>";
String formattedXml = new [UnknownClass]().format(unformattedXml);

Lưu ý: Đầu vào của tôi là một Chuỗi . Đầu ra của tôi là một Chuỗi .

(Cơ bản) kết quả giả:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <tag>
    <nested>hello</nested>
  </tag>
</root>

kiểm tra câu hỏi này: stackoverflow.com/questions/1264849/
Mạnh

10
Chỉ tò mò, bạn đang gửi đầu ra này đến một tệp XML hoặc một cái gì đó khác mà việc thụt lề thực sự quan trọng? Cách đây một thời gian, tôi đã rất lo lắng về việc định dạng XML của mình để hiển thị đúng cách ... nhưng sau khi dành rất nhiều thời gian cho việc này, tôi nhận ra rằng tôi phải gửi đầu ra của mình tới trình duyệt web và bất kỳ trình duyệt web tương đối hiện đại nào sẽ thực sự hiển thị XML trong một cấu trúc cây đẹp, vì vậy tôi có thể quên vấn đề này và tiếp tục. Tôi đang đề cập đến điều này chỉ trong trường hợp bạn (hoặc người dùng khác có cùng vấn đề) có thể đã bỏ qua cùng một chi tiết.
Abel Morelos

3
@ Tin tưởng, lưu vào các tệp văn bản, chèn vào văn bản HTML và chuyển sang bảng điều khiển cho mục đích gỡ lỗi.
Steve McLeod

2
"giữ quá rộng" - thật khó để chính xác hơn câu hỏi hiện tại!
Steve McLeod

Câu trả lời:


265
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);

Lưu ý: Kết quả có thể thay đổi tùy thuộc vào phiên bản Java. Tìm kiếm cách giải quyết cụ thể cho nền tảng của bạn.


1
Làm thế nào để làm cho đầu ra không chứa <?xml version="1.0" encoding="UTF-8"?>?
Thắng Phạm

19
Để bỏ qua <?xml ...>khai báo, hãy thêmtransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
rustyx

4
Độc giả bình thường có thể tìm thấy một phiên bản cải tiến của giải pháp được mô tả ở đây ( stackoverflow.com/a/33541820/363573 ).
Stephan

5
được docđịnh nghĩa ở đâu?
Florian F

6
Điều này không trả lời câu hỏi của tôi: làm cách nào để định dạng Chuỗi chứa XML? Câu trả lời này đã giả định rằng bằng cách nào đó bạn đã chuyển đổi đối tượng String thành đối tượng khác.
Steve McLeod

135

Đây là một câu trả lời cho câu hỏi của riêng tôi. Tôi đã kết hợp các câu trả lời từ các kết quả khác nhau để viết một lớp in XML đẹp.

Không đảm bảo về cách nó phản hồi với XML hoặc tài liệu lớn không hợp lệ.

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));
    }

}

13
Chỉ cần lưu ý rằng câu trả lời này yêu cầu sử dụng Xerces. Nếu bạn không muốn thêm phụ thuộc này thì bạn chỉ cần sử dụng các thư viện jdk tiêu chuẩn và javax.xml.transform.Transformer (xem câu trả lời của tôi bên dưới)
khylo

45
Quay trở lại năm 2008, đây là một câu trả lời hay, nhưng bây giờ tất cả có thể được thực hiện với các lớp JDK tiêu chuẩn thay vì các lớp Apache. Xem xerces.apache.org/xerces2-j/faq-general.html#faq-6 . Có, đây là Câu hỏi thường gặp về Xerces nhưng câu trả lời bao gồm các lớp JDK tiêu chuẩn. Việc triển khai 1.5 ban đầu của các lớp này có nhiều vấn đề nhưng mọi thứ đều hoạt động tốt từ 1.6 trở đi. Sao chép ví dụ LSSerializer trong Câu hỏi thường gặp, cắt bit "..." và thêm writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);vào sau LSSerializer writer = ...dòng.
George Hawkins

2
Tôi đã tạo một lớp nhỏ bằng cách sử dụng ví dụ mà Apache đưa ra, mà @GeorgeHawkins đã đưa ra một liên kết đến. Nó đã thiếu cách biến documentđược khởi tạo, vì vậy tôi nghĩ rằng tôi có thể thêm vào việc giảm tốc và làm một ví dụ nhanh về nó. Hãy cho tôi biết nếu tôi nên thay đổi một cái gì đó, pastebin.com/XL7932aC
samwell

không đúng khi bạn chỉ có thể làm điều đó với jdk. ít nhất là không đáng tin cậy nó phụ thuộc vào một số thực thi đăng ký nội bộ không hoạt động với jdk7u72 của tôi theo mặc định. Vì vậy, bạn vẫn tốt hơn sử dụng các công cụ apache trực tiếp.
dùng1050755

Đây là một giải pháp mà không có bất kỳ sự phụ thuộc nào: stackoverflow.com/a/33541820/363573 .
Stephan

131

một giải pháp đơn giản hơn dựa trên câu trả lời này :

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);
        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);
}

thử nghiệm:

prettyFormat("<root><child>aaa</child><child/></root>");

trả về:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <child>aaa</child>
  <child/>
</root>

1
Đây là mã tôi luôn sử dụng nhưng tại công ty này nó không hoạt động, tôi cho rằng họ đang sử dụng một thư viện chuyển đổi XML khác. Tôi đã tạo ra nhà máy như một dòng riêng biệt và sau đó đã làm factory.setAttribute("indent-number", 4);và bây giờ nó hoạt động.
Adrian Smith

Làm thế nào để làm cho đầu ra không chứa <?xml version="1.0" encoding="UTF-8"?>?
Thắng Phạm

4
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
@Harry

5
Xin chào Tôi đang sử dụng mã chính xác này và định dạng của tôi đúng với ngoại lệ của phần tử đầu tiên Vì vậy, đây: <?xml version="1.0" encoding="UTF-8"?><root>tất cả nằm trên một dòng. Bất cứ ý tưởng tại sao?
CodyK

2
@Codemiester: Có vẻ là một lỗi (xem stackoverflow.com/a/18251901/3375325 ). Thêm transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");làm việc cho tôi.
jansohn

99

Bây giờ là năm 2012 và Java có thể làm nhiều hơn là nó được sử dụng để với XML, tôi muốn thêm một thay thế cho câu trả lời chấp nhận của tôi. Điều này không có phụ thuộc bên ngoài 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));
    }
}

Không có sự chú ý, nhưng nó hoạt động với điều này: System.setProperty (DOMImcellenceationRegistry.PROPERTY, "com.sun.org.apache.xerces.i INTERNal.dom.DOMImcellenceationSourceImpl");
ggb667

1
Làm thế nào để bạn thêm sự chú ý vào ví dụ này?
ggb667 20/03/13

2
@DanTemple Có vẻ như bạn cần sử dụng LSOutput để kiểm soát mã hóa. Xem chipkillmar.net/2009/03/25/pretty-print-xml-from-a-dom
Joshua Davis

1
Tôi đã cố gắng sử dụng điều này trong Andriod nhưng tôi không thể tìm thấy gói `DOMImcellenceationRegistry. Tôi đang sử dụng java 8.
Chintan Soni

2
cảm ơn vì đã bao gồm cả danh sách nhập khẩu, rất nhiều gói xung đột có sẵn để hiểu ý nghĩa của sự kết hợp cần thiết ..
Leon

54

Chỉ cần lưu ý rằng câu trả lời được xếp hạng hàng đầu yêu cầu sử dụng xerces.

Nếu bạn không muốn thêm phụ thuộc bên ngoài này thì bạn chỉ cần sử dụng các thư viện jdk tiêu chuẩn (thực sự được xây dựng bằng xerces trong nội bộ).

NB Có một lỗi với jdk phiên bản 1.5, hãy xem http://bugs.sun.com/bugdatabase/view_orms.do?orms_id=6296446 nhưng hiện đã được giải quyết.,

(Lưu ý nếu xảy ra lỗi, điều này sẽ trả về văn bản gốc)

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;
        }
    }

}

Trong trường hợp này, các tab trái không được sử dụng. Tất cả các thẻ bắt đầu tại biểu tượng đầu tiên của dòng, giống như văn bản thông thường.
Ruslan

bạn không cần chỉ định bộ ký tự khi chuyển đổi qua lại giữa byte và chuỗi?
Will Glass

2
Không cần phải chuyển đổi từ và sang mảng byte / Chuỗi. Ít nhất bạn sẽ phải chỉ định bộ ký tự khi làm như vậy. Tùy chọn tốt hơn sẽ là sử dụng các lớp StringReader và StringWriter được gói trong InputSource và StreamResult.
maximdim

không làm việc. bạn cần phải loay hoay với một số thực hiện đăng ký nội bộ.
dùng1050755

Đây là một biến thể đơn giản hơn của giải pháp này: stackoverflow.com/a/33541820/363573
Stephan

32

Trước đây tôi đã in khá nhiều bằng phương thức 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();
}

3
Giải pháp được chấp nhận không thụt lề đúng cách các thẻ lồng nhau trong trường hợp của tôi, cái này thì có.
Đuổi theo Seibert

3
Tôi đã sử dụng kết hợp này với việc loại bỏ tất cả các dấu cách ở cuối dòng:prettyPrintedString.replaceAll("\\s+\n", "\n")
jediz

19

Đây là một cách để làm điều đó bằng cách sử dụng dom4j :

Nhập khẩu:

import org.dom4j.Document;  
import org.dom4j.DocumentHelper;  
import org.dom4j.io.OutputFormat;  
import org.dom4j.io.XMLWriter;

Mã số:

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();

1
Đây didnt làm việc cho tôi. Nó chỉ đưa ra một cái gì đó như: <?xml version...trên một dòng và mọi thứ khác trên một dòng khác.
Sixty feetersdude

14

Vì bạn đang bắt đầu với a String, bạn cần chuyển đổi sang một DOMđối tượng (ví dụ Node) trước khi bạn có thể sử dụng Transformer. Tuy nhiên, nếu bạn biết chuỗi XML của mình là hợp lệ và bạn không muốn phải chịu chi phí bộ nhớ khi phân tích chuỗi thành DOM, sau đó chạy một biến đổi qua DOM để lấy lại chuỗi - bạn có thể thực hiện một số lỗi thời ký tự bằng cách phân tích ký tự. Chèn một dòng mới và khoảng trắng sau mỗi </...>ký tự, giữ và thụt lề (để xác định số lượng khoảng trắng) mà bạn tăng cho mỗi <...>và giảm cho mỗi </...>bạn nhìn thấy.

Tuyên bố miễn trừ trách nhiệm - Tôi đã thực hiện chỉnh sửa cắt / dán / văn bản các chức năng bên dưới, vì vậy chúng có thể không biên dịch như hiện tại.

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));
}

1
"Tuy nhiên, nếu bạn biết chuỗi XML của bạn hợp lệ ..." điểm tốt. Xem giải pháp của tôi dựa trên phương pháp này dưới đây.
David Easley

12

Nếu sử dụng thư viện XML của bên thứ 3 là ổn, bạn có thể thoát khỏi một cái gì đó đơn giản hơn đáng kể so với những gì câu trả lời được bình chọn cao nhất hiện nay .

Nó đã được tuyên bố rằng cả đầu vào và đầu ra phải là Chuỗi, vì vậy đây là một phương thức tiện ích thực hiện điều đó, được triển khai với thư viện 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");
}

Tôi đã thử nghiệm rằng nó hoạt động và kết quả không phụ thuộc vào phiên bản JRE của bạn hoặc bất cứ điều gì tương tự. Để xem cách tùy chỉnh định dạng đầu ra theo ý thích của bạn, hãy xem SerializerAPI.

Điều này thực sự xuất hiện lâu hơn tôi nghĩ - một số dòng bổ sung là cần thiết bởi vì Serializermuốn OutputStreamviết cho. Nhưng lưu ý rằng có rất ít mã cho việc xoay vòng XML thực tế ở đây.

(Câu trả lời này là một phần của đánh giá của tôi về XOM, được đề nghị như là một tùy chọn trong tôi câu hỏi về tốt nhất thư viện XML Java để thay thế dom4j Đối với hồ sơ, với dom4j bạn có thể đạt được điều này một cách dễ dàng tương tự sử dụng. XMLWriterOutputFormat. Chỉnh sửa : .. .as thể hiện trong câu trả lời của mlo55 .)


2
Cảm ơn, đó là những gì tôi đang tìm kiếm. Nếu bạn có một XML đã được phân tích cú pháp bằng XOM trong một đối tượng "Tài liệu", bạn có thể chuyển nó trực tiếp đến serializer.write (document);
Thibault D.

12

Kevin Hakanson nói: "Tuy nhiên, nếu bạn biết chuỗi XML của mình hợp lệ và bạn không muốn phải chịu chi phí bộ nhớ khi phân tích chuỗi thành DOM, sau đó chạy một biến đổi qua DOM để lấy lại chuỗi - bạn có thể chỉ thực hiện một số ký tự lỗi thời bằng cách phân tích ký tự. Chèn một dòng mới và khoảng trắng sau mỗi ký tự, giữ và thụt lề (để xác định số lượng khoảng trắng) mà bạn tăng cho mỗi <...> và giảm cho mỗi lần bạn nhìn thấy. "

Đã đồng ý. Cách tiếp cận như vậy là nhanh hơn nhiều và có ít phụ thuộc hơn.

Giải pháp ví dụ:

/**
 * 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
}

2
Đây là cách nó nên được thực hiện. Định dạng trên bay ở cấp chuỗi. Đây là giải pháp duy nhất sẽ định dạng XML không hợp lệ hoặc không đầy đủ.
Florian F

11

Hmmm ... phải đối mặt với một cái gì đó như thế này và đó là một lỗi đã biết ... chỉ cần thêm OutputProperty này ..

transformer.setOutputProperty(OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "8");

Hi vọng điêu nay co ich ...


2
Trường hợp đầu ra này có nguồn gốc từ đâu?
helenov

nhập com.sun.org.apache.xml.i INTERNal.serializer. *;
gaurav

9

Về nhận xét rằng "trước tiên bạn phải xây dựng một cây DOM": Không, bạn không cần và không nên làm điều đó.

Thay vào đó, hãy tạo StreamSource (StreamSource mới (StringReader mới (str)) và cung cấp thông tin đó cho biến áp nhận dạng được đề cập. Điều đó sẽ sử dụng trình phân tích cú pháp SAX và kết quả sẽ nhanh hơn nhiều. Nếu không, câu trả lời hàng đầu là tốt.


1
Tôi hoàn toàn đồng ý: xây dựng cây DOM trung gian là một sự lãng phí bộ nhớ. Thansk cho câu trả lời đó.
Florian F

9

Sử dụng scala:

import xml._
val xml = XML.loadString("<tag><nested>hello</nested></tag>")
val formatted = new PrettyPrinter(150, 2).format(xml)
println(formatted)

Bạn cũng có thể làm điều này trong Java, nếu bạn phụ thuộc vào scala-library.jar. Nó trông như thế này:

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);
    }
}

Đối PrettyPrintertượng được xây dựng với hai int, đầu tiên là chiều dài dòng tối đa và thứ hai là bước thụt.


9

phiên bản cải tiến nhẹ từ 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();
} 

repeatString (stack ++) ở đâu; phương pháp..?
dùng1912935

2
private static String repeatString (int stack) {StringBuilder indent = new StringBuilder (); for (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
Codekraps

Việc thụt lề không hoạt động tốt ở các thẻ kết thúc. Bạn cần thay đổi } else if (row.startsWith("</")) {một phần thành điều này:else if (row.startsWith("</")) { String indent = repeatIdent(--stack); if (pretty.charAt(pretty.length() - 1) == '\n') { pretty.append(indent + row + "\n"); } else { pretty.append(row + "\n"); } }
Csaba Tenkes

8

Chỉ để tham khảo trong tương lai, đây là một giải pháp hiệu quả với tôi (nhờ một bình luận mà @George Hawkins đã đăng trong một trong những câu trả lời):

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());

6

Nếu bạn chắc chắn rằng bạn có một XML hợp lệ, thì cái này đơn giản và tránh các cây XML DOM. Có thể có một số lỗi, hãy bình luận nếu bạn thấy bất cứ điều gì

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();
    }

2
Phương thức repeatString ở đâu ..?
dùng1912935

3
private static String repeatString (int stack) {StringBuilder indent = new StringBuilder (); for (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
Codekraps

Có [user1912935], những gì @codeskraps đã viết, nên đủ đơn giản :)
milosmns

Sự kết hợp với StringBuilder bên trong một vòng lặp: Thực tiễn tồi.
james.garriss

@ james.garriss Nhưng thật dễ dàng để phân chia thành các dòng mới, điều này chỉ minh họa một cách tiếp cận đơn giản mà không cần bất kỳ cây DOM nào.
milosmns

5

Tất cả các giải pháp trên không hiệu quả với tôi, sau đó tôi tìm thấy http://myshittycode.com/2014/02/10/java-properly-indenting-xml-opes/

Manh mối xóa bỏ khoảng trắng với 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();
}

1
Lưu ý rằng việc sử dụng thuộc tính '{ xml.apache.org/xslt } indent-lượng' sẽ ràng buộc bạn với việc triển khai biến áp cụ thể.
vallismortis

1
Từ tất cả các giải pháp này làm việc tốt nhất. Tôi đã có không gian và các dòng mới trong XML cộng với tôi không muốn thêm nhiều phụ thuộc vào dự án của mình. Tôi ước tôi không phải phân tích cú pháp XML nhưng ồ.
Fabio

5

Mã dưới đây hoạt động hoàn hảo

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);
    }
}

5

Tôi trộn tất cả chúng và viết một chương trình nhỏ. Nó đang đọc từ tệp xml và in ra. Chỉ thay vì xzy cho đường dẫn tập tin của bạn.

    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();

}

4

Chỉ là một giải pháp khác phù hợp với chúng tôi

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();
}

3

Sử dụng 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)));

3

Thay thế cho các câu trả lời từ max , codkraps , David Easleymilosmns , hãy xem thư viện máy in đẹp, hiệu năng cao, nhẹ của tôi: 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
}

Đôi khi, giống như khi chạy các dịch vụ SOAP được mô phỏng trực tiếp từ tệp, thật tốt khi có một máy in đẹp cũng xử lý XML đã được in đẹp:

PrettyPrinter prettyPrinter = PrettyPrinterBuilder.newPrettyPrinter().ignoreWhitespace().build();

Như một số người đã nhận xét, in ấn đẹp chỉ là một cách trình bày XML ở dạng dễ đọc hơn - khoảng trắng hoàn toàn không thuộc về dữ liệu XML của bạn.

Thư viện được thiết kế để in đẹp cho mục đích ghi nhật ký và cũng bao gồm các chức năng lọc (xóa bỏ ẩn danh / ẩn danh) và in đẹp XML trong các nút CDATA và văn bản.


2

Tôi đã có cùng một vấn đề và tôi đang có thành công lớn với JTidy ( http://jtidy.sourceforge.net/index.html )

Thí dụ:

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();

2

Underscore-java có phương thức tĩnh U.formatXml(string). Tôi là người duy trì dự án. Ví dụ sống

import com.github.underscore.lodash.U;

public class MyClass {
    public static void main(String args[]) {
        String xml = "<tag><nested>hello</nested></tag>";

        System.out.println(U.formatXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>" + xml + "</root>"));
    }
}

Đầu ra:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <tag>
      <nested>hello</nested>
   </tag>
</root>

Điều này thật tuyệt!
senyor

1

có một tiện ích xml dòng lệnh rất hay được gọi là xmlstarlet ( http://xmlstar.sourceforge.net/ ) có thể làm rất nhiều thứ mà rất nhiều người sử dụng.

Bạn có thể thực hiện chương trình này bằng lập trình bằng Runtime.exec và sau đó đọc tệp đầu ra được định dạng. Nó có nhiều tùy chọn hơn và báo cáo lỗi tốt hơn một vài dòng mã Java có thể cung cấp.

tải xuống xmlstarlet: http://sourceforge.net/project/showfiles.php?group_id=66612&package_id=64589


1

Tôi đã thấy rằng trong Java 1.6.0_32, phương thức bình thường để in một chuỗi XML (sử dụng Transformer với null hoặc xslt nhận dạng) không hoạt động như tôi muốn nếu các thẻ chỉ được phân tách bằng khoảng trắng, trái ngược với việc không có phân tách bản văn. Tôi đã thử sử dụng <xsl:strip-space elements="*"/>trong mẫu của tôi nhưng không có kết quả. Giải pháp đơn giản nhất mà tôi tìm thấy là tước không gian theo cách tôi muốn bằng bộ lọc SAXSource và XML. Vì giải pháp của tôi là ghi nhật ký, tôi cũng đã mở rộng nó để hoạt động với các đoạn XML chưa hoàn chỉnh. Lưu ý rằng phương thức bình thường có vẻ hoạt động tốt nếu bạn sử dụng DOMSource nhưng tôi không muốn sử dụng phương thức này vì không đầy đủ và chi phí bộ nhớ.

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();
    }

1

Các giải pháp tôi đã tìm thấy ở đây cho Java 1.6+ không định dạng lại mã nếu nó đã được định dạng. Mã làm việc cho tôi (và được định dạng lại mã đã được định dạng) là như sau.

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);
    }
}

Nó là một công cụ tốt để sử dụng trong các bài kiểm tra đơn vị của bạn để so sánh xml toàn chuỗi.

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);
}

1

Đối với những người tìm kiếm một giải pháp nhanh và bẩn - không cần XML phải hợp lệ 100%. ví dụ: trong trường hợp ghi nhật ký REST / SOAP (bạn không bao giờ biết những gì người khác gửi ;-))

Tôi đã tìm và nâng cao một đoạn mã được tìm thấy trên mạng mà tôi nghĩ vẫn còn thiếu ở đây như một cách tiếp cận hợp lệ:

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));
}

đây là đầu ra:

<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>

1

Tôi đã thấy một câu trả lời bằng cách sử dụng Scala, vì vậy đây là một câu trả lời khác Groovy, chỉ trong trường hợp ai đó thấy nó thú vị. Việc thụt lề mặc định là 2 bước, hàm XmlNodePrintertạo cũng có thể được chuyển qua một giá trị khác.

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()

Sử dụng từ Java nếu jar Groovy nằm trong classpath

  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());

1

Trong trường hợp bạn không cần thụt quá nhiều nhưng chỉ cần ngắt một vài dòng, chỉ cần regex ...

String leastPrettifiedXml = uglyXml.replaceAll("><", ">\n<");

Mã này là tốt đẹp, không phải là kết quả vì thiếu thụt lề.


(Đối với các giải pháp với thụt đầu dòng, xem câu trả lời khác.)


1
Hmmmm ... Chỉ cần suy nghĩ lớn, ai sẽ cần giải pháp như vậy? Khu vực duy nhất tôi có thể thấy là dữ liệu chúng tôi nhận được từ một số dịch vụ web và chỉ để kiểm tra dữ liệu đó và tính hợp lệ của nó, nhà phát triển hoặc người kiểm tra có thể cần những dữ liệu dễ dàng như vậy. Nếu không thì không phải là một lựa chọn tốt ....
Sudhakar Chavali

1
@SudhakarChavali tôi là nhà phát triển. tôi có thể cần điều đó cho các bản hack println () và log.debug () bẩn; tức là đôi khi tôi chỉ có thể sử dụng các tệp nhật ký từ trong môi trường máy chủ bị hạn chế (với giao diện quản trị web thay vì truy cập shell) thay vì từng bước gỡ lỗi chương trình.
comonad
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.