Bagaimana cara mencetak XML dari Java?

442

Saya memiliki String Java yang berisi XML, tanpa umpan baris atau lekukan. Saya ingin mengubahnya menjadi sebuah String dengan XML yang diformat dengan baik. Bagaimana saya melakukan ini?

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

Catatan: Input saya adalah sebuah String . Output saya adalah sebuah String .

(Dasar) hasil tiruan:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <tag>
    <nested>hello</nested>
  </tag>
</root>
Steve McLeod
sumber
periksa pertanyaan ini: stackoverflow.com/questions/1264849/…
dfa
10
Hanya ingin tahu, apakah Anda mengirimkan output ini ke file XML atau sesuatu yang lain di mana indentasi benar-benar penting? Beberapa waktu yang lalu saya sangat khawatir tentang memformat XML saya agar dapat ditampilkan dengan benar ... tetapi setelah menghabiskan banyak waktu untuk hal ini, saya menyadari bahwa saya harus mengirim output ke browser web, dan browser web yang relatif modern sebenarnya akan menampilkan XML dalam struktur pohon yang bagus, jadi saya bisa melupakan masalah ini dan melanjutkan. Saya menyebutkan ini untuk berjaga-jaga jika Anda (atau pengguna lain dengan masalah yang sama) dapat mengabaikan detail yang sama.
Abel Morelos
3
@ Bel, menyimpan ke file teks, memasukkan ke dalam HTML textareas, dan membuang ke konsol untuk keperluan debugging.
Steve McLeod
2
"ditahan sebagai terlalu luas" - sulit untuk lebih tepat daripada pertanyaan saat ini!
Steve McLeod

Jawaban:

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

Catatan: Hasil dapat bervariasi tergantung pada versi Java. Cari solusi untuk platform Anda.

Lorenzo Boccaccia
sumber
1
Bagaimana cara membuat agar output tidak mengandung <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham
19
Untuk menghilangkan <?xml ...>deklarasi, tambahkantransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
rustyx
4
Pembaca biasa mungkin menemukan versi perbaikan dari solusi yang dijelaskan di sini ( stackoverflow.com/a/33541820/363573 ).
Stephan,
5
dimana docdidefinisikan?
Florian F
6
Ini tidak menjawab pertanyaan saya: bagaimana cara memformat String yang berisi XML? Jawaban ini sudah mengasumsikan bahwa Anda entah bagaimana mengubah objek String ke objek lain.
Steve McLeod
135

Inilah jawaban untuk pertanyaan saya sendiri. Saya menggabungkan jawaban dari berbagai hasil untuk menulis kelas yang cukup mencetak XML.

Tidak ada jaminan tentang responsnya dengan XML atau dokumen besar yang tidak valid.

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

}
Steve McLeod
sumber
13
Hanya untuk mencatat bahwa jawaban ini memerlukan penggunaan Xerces. Jika Anda tidak ingin menambahkan ketergantungan ini maka Anda cukup menggunakan pustaka jdk standar dan javax.xml.transform.Transformer (lihat jawaban saya di bawah)
khylo
45
Kembali pada tahun 2008 ini adalah jawaban yang baik, tetapi sekarang ini semua dapat dilakukan dengan kelas JDK standar daripada kelas Apache. Lihat xerces.apache.org/xerces2-j/faq-general.html#faq-6 . Ya ini adalah Xerces FAQ tetapi jawabannya mencakup kelas JDK standar. Implementasi awal 1,5 dari kelas-kelas ini memiliki banyak masalah tetapi semuanya berjalan dengan baik mulai dari 1.6. Salin contoh LSSerializer di FAQ, potong bit "..." dan tambahkan writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);setelah LSSerializer writer = ...baris.
George Hawkins
2
Saya telah membuat kelas kecil menggunakan contoh yang diberikan oleh Apache, yang diberi tautan oleh @GeorgeHawkins. Itu hilang bagaimana variabel documentdiinisialisasi, jadi saya pikir saya mungkin menambahkan deselerasi dan membuat contoh cepat dari itu. Beri tahu saya jika saya harus mengubah sesuatu, pastebin.com/XL7932aC
samwell
tidak benar bahwa Anda dapat melakukannya hanya dengan jdk. setidaknya tidak andal. itu tergantung pada beberapa implementasi registri internal yang tidak aktif dengan jdk7u72 saya secara default. jadi Anda masih lebih baik menggunakan hal-hal apache secara langsung.
user1050755
Ini adalah solusi tanpa ketergantungan: stackoverflow.com/a/33541820/363573 .
Stephan,
131

solusi yang lebih sederhana berdasarkan jawaban ini :

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

Kasus cobaan:

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

pengembalian:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <child>aaa</child>
  <child/>
</root>
dfa
sumber
1
Ini adalah kode yang selalu saya gunakan tetapi di perusahaan ini tidak berfungsi, saya menganggap mereka menggunakan pustaka transformasi XML lain. Saya membuat pabrik sebagai jalur terpisah dan kemudian melakukannya factory.setAttribute("indent-number", 4);dan sekarang berfungsi.
Adrian Smith
Bagaimana cara membuat agar output tidak mengandung <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham
4
@ Harry:transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
jjmontes
5
Hai Saya menggunakan kode persis ini, dan menambang format dengan benar dengan pengecualian elemen pertama Jadi, ini: <?xml version="1.0" encoding="UTF-8"?><root>semua dalam satu baris. Ada ide mengapa?
CodyK
2
@ Codemiester: Tampaknya menjadi bug (lihat stackoverflow.com/a/18251901/3375325 ). Menambahkan transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");bekerja untuk saya.
jansohn
100

Sekarang ini tahun 2012 dan Java dapat melakukan lebih banyak daripada biasanya dengan XML, saya ingin menambahkan alternatif untuk jawaban yang saya terima. Ini tidak memiliki dependensi di luar 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));
    }
}
Steve McLeod
sumber
Tidak ada indentasi, tetapi berfungsi dengan ini: System.setProperty (DOMImplementationRegistry.PROPERTY, "com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");
ggb667
1
Bagaimana Anda menambahkan indentasi pada contoh ini?
ggb667
2
@DanTemple Sepertinya Anda perlu menggunakan LSOutput untuk mengontrol penyandian. Lihat chipkillmar.net/2009/03/25/pretty-print-xml-from-a-dom
Joshua Davis
1
Saya mencoba menggunakan ini di Andriod tetapi saya tidak dapat menemukan paket `DOMImplementationRegistry. Saya menggunakan java 8.
Chintan Soni
2
terima kasih telah memasukkan daftar impor juga, begitu banyak paket yang saling bertentangan tersedia untuk memahami kombinasi yang diperlukan jika tidak ..
Leon
54

Hanya untuk mencatat bahwa jawaban berperingkat teratas membutuhkan penggunaan xerces.

Jika Anda tidak ingin menambahkan ketergantungan eksternal ini maka Anda cukup menggunakan pustaka jdk standar (yang sebenarnya dibangun menggunakan xerces secara internal).

NB Ada bug dengan versi jdk 1.5 lihat http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446 tetapi sudah diatasi sekarang.,

(Perhatikan jika terjadi kesalahan ini akan mengembalikan teks asli)

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

}
khylo
sumber
Dalam hal ini tab kiri tidak digunakan. Semua tag dimulai pada simbol pertama dari garis, seperti teks biasa.
Ruslan
Anda tidak perlu menentukan charset saat mengkonversi bolak-balik antara byte dan string?
Will Glass
2
Seharusnya tidak perlu mengkonversi dari dan ke byte array / String. Paling tidak Anda harus menentukan charset ketika melakukannya. Opsi yang lebih baik adalah menggunakan kelas StringReader dan StringWriter yang dibungkus dengan InputSource dan StreamResult.
maximdim
tidak bekerja Anda perlu dipusingkan dengan beberapa implementasi registri internal.
user1050755
Berikut adalah varian yang lebih sederhana dari solusi ini: stackoverflow.com/a/33541820/363573
Stephan
32

Aku sudah cukup dicetak di masa lalu menggunakan org.dom4j.io.OutputFormat.createPrettyPrint () metode

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();
}
mlo55
sumber
3
Solusi yang diterima tidak membuat indentasi tag bersarang dengan benar dalam kasus saya, yang satu ini benar.
Chase Seibert
3
Saya menggunakan ini dalam konjungsi dengan menghapus semua spasi di akhir baris:prettyPrintedString.replaceAll("\\s+\n", "\n")
jediz
19

Berikut cara melakukannya menggunakan dom4j :

Impor:

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

Kode:

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();
Markus Paus
sumber
1
Ini tidak berhasil untuk saya. Itu hanya memberi sesuatu seperti: <?xml version...pada satu baris dan semua lainnya pada baris lain.
sixtyfootersdude
14

Karena Anda mulai dengan a String, Anda perlu menyamarkan suatu DOMobjek (mis. Node) Sebelum Anda dapat menggunakan Transformer. Namun, jika Anda tahu string XML Anda valid, dan Anda tidak ingin mengeluarkan overhead memori dari penguraian string ke DOM, kemudian jalankan transformasi di atas DOM untuk mendapatkan string kembali - Anda bisa melakukan beberapa cara lama karakter dengan karakter parsing. Masukkan baris dan spasi baru setelah setiap </...>karakter, pertahankan dan indentasi penghitung (untuk menentukan jumlah spasi) yang Anda tambahkan untuk setiap <...>dan pengurangan untuk setiap yang </...>Anda lihat.

Penafian - Saya melakukan edit cut / paste / teks dari fungsi-fungsi di bawah ini, sehingga mereka tidak dapat dikompilasi seperti apa adanya.

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
sumber
1
"Namun, jika Anda tahu string XML Anda valid ..." bagus. Lihat solusi saya berdasarkan pendekatan ini di bawah ini.
David Easley
12

Jika menggunakan pustaka XML pihak ke-3 ok, Anda bisa lolos dengan sesuatu yang secara signifikan lebih sederhana daripada yang disarankan oleh jawaban terpilih saat ini .

Dinyatakan bahwa input dan output haruslah Strings, jadi inilah metode utilitas yang melakukan hal itu, diimplementasikan dengan pustaka 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");
}

Saya menguji apakah itu berhasil, dan hasilnya tidak bergantung pada versi JRE Anda atau yang seperti itu. Untuk melihat bagaimana menyesuaikan format output sesuai keinginan Anda, lihat SerializerAPI.

Ini sebenarnya keluar lebih lama dari yang saya kira - beberapa baris tambahan diperlukan karena Serializeringin OutputStreammenulis. Tetapi perhatikan bahwa ada sangat sedikit kode untuk twiddling XML aktual di sini.

(Jawaban ini adalah bagian dari evaluasi XOM saya, yang disarankan sebagai salah satu opsi dalam pertanyaan saya tentang perpustakaan XML Java terbaik untuk menggantikan dom4j. Sebagai catatan, dengan dom4j Anda dapat mencapai ini dengan mudah menggunakan XMLWriterdan OutputFormat. Edit : .. .sebagai ditunjukkan dalam jawaban mlo55 .)

Jonik
sumber
2
Terima kasih, itulah yang saya cari. Jika Anda memiliki XML yang sudah diuraikan dengan XOM di objek "Dokumen", Anda bisa meneruskannya langsung ke serializer.write (document);
Thibault D.
12

Kevin Hakanson berkata: "Namun, jika Anda tahu string XML Anda valid, dan Anda tidak ingin mengeluarkan memori dari penguraian string ke DOM, kemudian jalankan transformasi di atas DOM untuk mendapatkan string kembali - Anda bisa cukup lakukan beberapa karakter kuno dengan penguraian karakter. Sisipkan baris dan spasi baru setelah setiap karakter, simpan dan indentasi penghitung (untuk menentukan jumlah spasi) yang Anda tambahkan untuk setiap <...> dan pengurangan untuk setiap yang Anda lihat. "

Sepakat. Pendekatan semacam itu jauh lebih cepat dan memiliki ketergantungan yang jauh lebih sedikit.

Contoh solusi:

/**
 * 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
}
David Easley
sumber
2
Ini adalah cara yang harus dilakukan. Format dengan cepat pada level string. Ini adalah satu-satunya solusi yang akan memformat XML yang tidak valid atau tidak lengkap.
Florian F
11

Hmmm ... menghadapi sesuatu seperti ini dan ini adalah bug yang dikenal ... cukup tambahkan OutputProperty ini ..

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

Semoga ini membantu ...

Sandeep Phukan
sumber
2
Dari mana OutputPropertiesFactory ini berasal?
helenov
import com.sun.org.apache.xml.internal.serializer. *;
gaurav
9

Mengenai komentar bahwa "Anda harus terlebih dahulu membangun pohon DOM": Tidak, Anda tidak perlu dan tidak boleh melakukannya.

Sebagai gantinya, buat StreamSource (new StreamSource (new StringReader (str)), dan masukkan itu ke transformator identitas yang disebutkan. Itu akan menggunakan parser SAX, dan hasilnya akan jauh lebih cepat. Membangun pohon perantara adalah overhead murni untuk kasus ini. Kalau tidak, jawaban berperingkat teratas itu baik.

StaxMan
sumber
1
Sepenuh hati saya setuju: membangun pohon DOM menengah adalah pemborosan memori. Bahasa Thansk untuk jawaban itu.
Florian F
9

Menggunakan scala:

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

Anda dapat melakukan ini di Jawa juga, jika Anda bergantung pada scala-library.jar. Ini terlihat seperti ini:

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

The PrettyPrinterobjek dibangun dengan dua ints, yang menjadi pertama panjang garis max dan yang kedua menjadi langkah lekukan.

Synesso
sumber
9

versi yang sedikit ditingkatkan dari 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();
} 
codekraps
sumber
di mana repeatString (stack ++); metode..?
user1912935
2
private static String repeatString (int stack) {StringBuilder indent = new StringBuilder (); untuk (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
codeskraps
Lekukan tidak bekerja dengan baik pada tag akhir. Anda perlu mengubah } else if (row.startsWith("</")) {bagian ini: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

Hanya untuk referensi di masa mendatang, inilah solusi yang sesuai untuk saya (terima kasih atas komentar yang @George Hawkins diposting di salah satu jawaban):

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());
Michael
sumber
6

Jika Anda yakin memiliki XML yang valid, ini sederhana, dan menghindari pohon XML DOM. Mungkin memiliki beberapa bug, lakukan komentar jika Anda melihat sesuatu

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();
    }
milosmns
sumber
2
dimana metode repeatString ..?
user1912935
3
private static String repeatString (int stack) {StringBuilder indent = new StringBuilder (); untuk (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
codeskraps
Ya [user1912935], apa yang ditulis oleh @codeskraps, harus cukup sederhana :)
milosmns
Penggabungan dengan StringBuilder di dalam lingkaran: Praktek yang buruk.
james.garriss
@ james.garriss Tapi sangat mudah untuk dipecah menjadi baris baru, ini hanya menggambarkan pendekatan sederhana tanpa pohon DOM.
milosmns
5

Semua solusi di atas tidak bekerja untuk saya, maka saya menemukan ini http://myshittycode.com/2014/02/10/java-properly-indenting-xml-string/

Petunjuknya adalah menghapus spasi putih dengan 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();
}
Georgy Gobozov
sumber
1
Perhatikan bahwa penggunaan properti indent- number '{ xml.apache.org/xslt } akan mengikat Anda dengan implementasi transformator tertentu.
vallismortis
1
Dari semua solusi yang satu ini yang terbaik. Saya sudah punya spasi dan baris baru di XML saya ditambah saya tidak ingin menambahkan lebih banyak dependensi ke proyek saya. Saya berharap saya tidak perlu menguraikan XML tetapi oh well.
Fabio
5

Kode di bawah ini berfungsi dengan baik

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);
    }
}
maks tkach
sumber
5

Saya mencampur semuanya dan menulis satu program kecil. Itu membaca dari file xml dan mencetak. Just Alih-alih xzy berikan path file Anda.

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

}
pengguna3083990
sumber
4

Hanya solusi lain yang bekerja untuk kita

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

Menggunakan 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)));
BijanE
sumber
3

Sebagai alternatif dari jawaban dari max , codeskraps , David Easley dan milosmns , lihat perpustakaan printer saya yang ringan dan berkinerja tinggi: 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
}

Terkadang, seperti ketika menjalankan layanan SOAP yang diejek langsung dari file, ada baiknya memiliki printer-cantik yang juga menangani XML yang sudah tercetak:

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

Seperti yang dikomentari oleh beberapa orang, pencetakan cantik hanyalah cara menyajikan XML dalam bentuk yang lebih bisa dibaca manusia - spasi putih tidak termasuk dalam data XML Anda.

Perpustakaan ini dimaksudkan untuk pencetakan cantik untuk keperluan pencatatan, dan juga mencakup fungsi untuk memfilter (penghilangan subtree / anonimisasi) dan pencetakan XML dalam node CDATA dan Teks.

ThomasRS
sumber
2

Saya memiliki masalah yang sama dan saya mengalami kesuksesan besar dengan JTidy ( http://jtidy.sourceforge.net/index.html )

Contoh:

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();
Kristoffer Lindvall
sumber
2

Underscore-java memiliki metode statis U.formatXml(string). Saya adalah pengelola proyek. Contoh langsung

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

Keluaran:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <tag>
      <nested>hello</nested>
   </tag>
</root>
Valentyn Kolesnikov
sumber
Ini luar biasa!
senyor
1

ada utilitas xml baris perintah yang sangat bagus yang disebut xmlstarlet ( http://xmlstar.sourceforge.net/ ) yang dapat melakukan banyak hal yang banyak orang gunakan.

Anda dapat menjalankan program ini secara terprogram menggunakan Runtime.exec dan kemudian membaca file output yang diformat. Ini memiliki lebih banyak opsi dan pelaporan kesalahan yang lebih baik daripada beberapa baris kode Java dapat menyediakan.

unduh xmlstarlet: http://sourceforge.net/project/showfiles.php?group_id=66612&package_id=64589

anjanb
sumber
1

Saya telah menemukan bahwa di Jawa 1.6.0_32 metode normal untuk cukup mencetak string XML (menggunakan Transformer dengan null atau identitas xslt) tidak berperilaku seperti yang saya inginkan jika tag hanya dipisahkan oleh spasi, bukan karena tidak memiliki pemisahan teks. Saya mencoba menggunakan <xsl:strip-space elements="*"/>dalam template saya tetapi tidak berhasil. Solusi paling sederhana yang saya temukan adalah menghapus ruang seperti yang saya inginkan menggunakan SAXSource dan filter XML. Karena solusi saya adalah untuk logging, saya juga memperluas ini untuk bekerja dengan fragmen XML yang tidak lengkap. Catatan metode normal tampaknya berfungsi dengan baik jika Anda menggunakan DOMSource tetapi saya tidak ingin menggunakan ini karena ketidaklengkapan dan overhead memori.

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

Solusi yang saya temukan di sini untuk Java 1.6+ tidak memformat ulang kode jika sudah diformat. Yang bekerja untuk saya (dan memformat ulang kode yang sudah diformat) adalah sebagai berikut.

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

Ini adalah alat yang baik untuk digunakan dalam pengujian unit Anda untuk perbandingan xml string penuh.

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);
}
Wojtek
sumber
1

Bagi mereka yang mencari solusi cepat dan kotor - yang tidak memerlukan XML 100% valid. mis. dalam kasus REST / SOAP logging (Anda tidak pernah tahu apa yang orang lain kirim ;-))

Saya menemukan dan menambahkan kode terpotong yang saya temukan online yang menurut saya masih hilang di sini sebagai pendekatan yang valid:

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

di sini adalah output:

<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>
maks
sumber
1

Saya melihat satu jawaban menggunakan Scala, jadi inilah jawaban lain Groovy, untuk berjaga-jaga seandainya seseorang menganggapnya menarik. Indentasi default adalah 2 langkah, XmlNodePrinterkonstruktor dapat melewati nilai lain juga.

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

Penggunaan dari Jawa jika jar asyik di 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());
vsnyc
sumber
1

Jika Anda tidak perlu lekukan yang banyak tetapi beberapa jeda baris, itu bisa cukup untuk hanya regex ...

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

Kode ini bagus, bukan hasil karena lekukan yang hilang.


(Untuk solusi dengan indentasi, lihat jawaban lain.)

comonad
sumber
1
Hmmmm ... Hanya berpikir keras, siapa yang akan membutuhkan solusi seperti itu? Satu-satunya area yang dapat saya lihat adalah data yang kami dapatkan dari beberapa layanan web dan hanya untuk menguji bahwa data dan validitasnya, pengembang atau penguji mungkin memerlukan yang semudah itu. Kalau tidak, bukan pilihan yang baik ....
Sudhakar Chavali
1
@SudhakarChavali saya seorang pengembang. saya mungkin memerlukan itu untuk hack println () dan log.debug () yang kotor; yaitu beberapa kali saya hanya dapat menggunakan file log dari dalam lingkungan server yang terbatas (dengan antarmuka admin web alih-alih akses shell) alih-alih waras langkah demi langkah debugging program.
comonad