Wie drucke ich XML aus Java?

442

Ich habe eine Java-Zeichenfolge, die XML enthält, ohne Zeilenvorschübe oder Einrückungen. Ich möchte daraus einen String mit schön formatiertem XML machen. Wie mache ich das?

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

Hinweis: Meine Eingabe ist eine Zeichenfolge . Meine Ausgabe ist ein String .

(Grund-) Scheinergebnis:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <tag>
    <nested>hello</nested>
  </tag>
</root>
Steve McLeod
quelle
Überprüfen Sie diese Frage: stackoverflow.com/questions/1264849/…
dfa
10
Nur neugierig, senden Sie diese Ausgabe an eine XML-Datei oder an etwas anderes, bei dem das Einrücken wirklich wichtig ist? Vor einiger Zeit war ich sehr besorgt darüber, mein XML zu formatieren, damit es richtig angezeigt wird ... aber nachdem ich einige Zeit damit verbracht hatte, wurde mir klar, dass ich meine Ausgabe an einen Webbrowser und einen relativ modernen Webbrowser senden musste wird das XML tatsächlich in einer schönen Baumstruktur anzeigen, so dass ich dieses Problem vergessen und weitermachen könnte. Ich erwähne dies nur für den Fall, dass Sie (oder ein anderer Benutzer mit demselben Problem) dasselbe Detail übersehen haben könnten.
Abel Morelos
3
@Abel, Speichern in Textdateien, Einfügen in HTML-Textbereiche und Speichern auf der Konsole zum Debuggen.
Steve McLeod
2
"als zu breit auf Eis legen" - es ist schwer genauer zu sein als die aktuelle Frage!
Steve McLeod

Antworten:

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

Hinweis: Die Ergebnisse können je nach Java-Version variieren. Suchen Sie nach plattformspezifischen Problemumgehungen.

Lorenzo Boccaccia
quelle
1
Wie mache ich das so, dass die Ausgabe nicht enthält <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham
19
Um die <?xml ...>Deklaration wegzulassen , fügen Sietransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
rustyx
4
Gelegenheitsleser finden möglicherweise eine verbesserte Version der hier beschriebenen Lösung nützlich ( stackoverflow.com/a/33541820/363573 ).
Stephan
5
wo ist docdefiniert?
Florian F
6
Dies beantwortet meine Frage nicht: Wie formatiere ich einen String, der XML enthält? Diese Antwort setzt bereits voraus, dass Sie das String-Objekt irgendwie in ein anderes Objekt konvertiert haben.
Steve McLeod
135

Hier ist eine Antwort auf meine eigene Frage. Ich habe die Antworten aus den verschiedenen Ergebnissen kombiniert, um eine Klasse zu schreiben, die hübsch XML druckt.

Keine Garantie dafür, wie es mit ungültigem XML oder großen Dokumenten reagiert.

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
quelle
13
Nur um zu beachten, dass diese Antwort die Verwendung von Xerces erfordert. Wenn Sie diese Abhängigkeit nicht hinzufügen möchten, können Sie einfach die Standard-JDK-Bibliotheken und javax.xml.transform.Transformer verwenden (siehe meine Antwort unten)
Khylo
45
Im Jahr 2008 war dies eine gute Antwort, aber jetzt kann dies alles mit Standard-JDK-Klassen anstelle von Apache-Klassen durchgeführt werden. Siehe xerces.apache.org/xerces2-j/faq-general.html#faq-6 . Ja, dies ist eine Xerces-FAQ, aber die Antwort deckt Standard-JDK-Klassen ab. Die anfängliche 1.5-Implementierung dieser Klassen hatte viele Probleme, aber ab 1.6 funktioniert alles einwandfrei. Kopieren Sie das LSSerializer-Beispiel in die FAQ, hacken Sie das Bit "..." und fügen Sie es writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);nach der LSSerializer writer = ...Zeile hinzu.
George Hawkins
2
Ich habe eine kleine Klasse mit dem Beispiel von Apache erstellt, zu dem @GeorgeHawkins einen Link gegeben hat. Es fehlte, wie die Variable documentinitialisiert wurde, daher dachte ich, ich könnte die Verzögerung hinzufügen und ein kurzes Beispiel daraus machen. Lassen Sie mich wissen , wenn ich etwas ändern sollte, pastebin.com/XL7932aC
Samwell
Es ist nicht wahr, dass Sie das nur mit JDK tun können. Zumindest nicht zuverlässig. Dies hängt von einer internen Registrierungsimplementierung ab, die mit meinem jdk7u72 standardmäßig nicht aktiv ist. Also solltest du das Apache-Zeug noch besser direkt verwenden.
user1050755
Hier ist eine Lösung ohne Abhängigkeiten: stackoverflow.com/a/33541820/363573 .
Stephan
131

Eine einfachere Lösung basierend auf dieser Antwort :

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

Testfall:

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

kehrt zurück:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <child>aaa</child>
  <child/>
</root>
dfa
quelle
1
Dies ist der Code, den ich immer verwendet habe, aber bei dieser Firma hat er nicht funktioniert. Ich gehe davon aus, dass sie eine andere XML-Transformationsbibliothek verwenden. Ich habe die Fabrik als separate Linie erstellt und dann factory.setAttribute("indent-number", 4);und jetzt funktioniert es.
Adrian Smith
Wie mache ich das so, dass die Ausgabe nicht enthält <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham
4
@ Harry:transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
Jjmontes
5
Hallo, ich verwende genau diesen Code und meine Formate mit Ausnahme des ersten Elements. Also, dies: <?xml version="1.0" encoding="UTF-8"?><root>ist alles in einer Zeile. Irgendwelche Ideen warum?
CodyK
2
@Codemiester: Scheint ein Fehler zu sein (siehe stackoverflow.com/a/18251901/3375325 ). Hinzufügen transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");hat bei mir funktioniert.
Johnohn
99

Jetzt ist es 2012 und Java kann mehr als früher mit XML. Ich möchte meiner akzeptierten Antwort eine Alternative hinzufügen. Dies hat keine Abhängigkeiten außerhalb von 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
quelle
Keine Einrückung, aber es funktioniert damit: System.setProperty (DOMImplementationRegistry.PROPERTY, "com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");
ggb667
1
Wie fügen Sie diesem Beispiel Einrückungen hinzu?
ggb667
2
@DanTemple Es scheint, als müssten Sie LSOutput verwenden, um die Codierung zu steuern. Siehe chipkillmar.net/2009/03/25/pretty-print-xml-from-a-dom
Joshua Davis
1
Ich habe versucht, dies in Andriod zu verwenden, aber ich kann das Paket `DOMImplementationRegistry nicht finden. Ich benutze Java 8.
Chintan Soni
2
Vielen Dank, dass Sie auch die Importliste hinzugefügt haben. Es sind so viele widersprüchliche Pakete verfügbar, dass die ansonsten erforderliche Kombination sinnvoll ist.
Leon
54

Nur um zu beachten, dass die am besten bewertete Antwort die Verwendung von xerces erfordert.

Wenn Sie diese externe Abhängigkeit nicht hinzufügen möchten, können Sie einfach die Standard-JDK-Bibliotheken verwenden (die tatsächlich intern mit xerces erstellt werden).

NB Es gab einen Fehler mit jdk Version 1.5, siehe http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446 , der jetzt behoben ist.

(Hinweis: Wenn ein Fehler auftritt, wird der Originaltext zurückgegeben.)

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
quelle
In diesem Fall werden die linken Registerkarten nicht verwendet. Alle Tags beginnen wie gewohnt mit dem ersten Symbol der Zeile.
Ruslan
Müssen Sie beim Konvertieren zwischen Bytes und Zeichenfolge keinen Zeichensatz angeben?
Will Glass
2
Es sollte nicht erforderlich sein, von und in Byte-Arrays / String zu konvertieren. Zumindest müssten Sie dabei einen Zeichensatz angeben. Eine bessere Option wäre die Verwendung von StringReader- und StringWriter-Klassen, die in InputSource und StreamResult eingeschlossen sind.
Maximdim
funktioniert nicht. Sie müssen mit einigen internen Registrierungsimplementierungen herumspielen.
user1050755
Hier ist eine einfachere Variante dieser Lösung: stackoverflow.com/a/33541820/363573
Stephan
32

Ich habe in der Vergangenheit mit der Methode org.dom4j.io.OutputFormat.createPrettyPrint () ziemlich gedruckt

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
quelle
3
Die akzeptierte Lösung rückt die verschachtelten Tags in meinem Fall nicht richtig ein, dies ist der Fall.
Chase Seibert
3
Ich habe dies in Verbindung mit dem Entfernen aller nachgestellten Leerzeichen am Ende der Zeilen verwendet:prettyPrintedString.replaceAll("\\s+\n", "\n")
jediz
19

Hier ist eine Möglichkeit, dies mit dom4j zu tun :

Importe:

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

Code:

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();
Mark Pope
quelle
1
Das hat bei mir nicht funktioniert. Es gab nur so etwas wie: <?xml version...in einer Zeile und alles andere in einer anderen Zeile.
Sixtyfootersdude
14

Da Sie mit a beginnen String, müssen Sie ein DOMObjekt (z. B. Node) verdecken, bevor Sie das verwenden können Transformer. Wenn Sie jedoch wissen, dass Ihre XML-Zeichenfolge gültig ist, und Sie nicht den Speicheraufwand für das Parsen einer Zeichenfolge in ein DOM aufbringen möchten, führen Sie eine Transformation über das DOM aus, um eine Zeichenfolge zurückzugewinnen Zeichen für Zeichen analysieren. Fügen Sie nach jedem </...>Zeichen eine neue Zeile und Leerzeichen ein , behalten Sie den Zähler bei und rücken Sie ihn ein (um die Anzahl der Leerzeichen zu bestimmen), die Sie für jedes Zeichen erhöhen und <...>für jedes, das </...>Sie sehen, verringern .

Haftungsausschluss - Ich habe die folgenden Funktionen ausgeschnitten / eingefügt / Text bearbeitet, sodass sie möglicherweise nicht so kompiliert werden, wie sie sind.

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
quelle
1
"Wenn Sie jedoch wissen, dass Ihre XML-Zeichenfolge gültig ist ..." Guter Punkt. Siehe meine Lösung basierend auf diesem Ansatz unten.
David Easley
12

Wenn die Verwendung einer XML-Bibliothek eines Drittanbieters in Ordnung ist, können Sie mit etwas wesentlich Einfacherem davonkommen, als es die derzeit am höchsten bewerteten Antworten vermuten lassen.

Es wurde angegeben, dass sowohl Eingabe als auch Ausgabe Zeichenfolgen sein sollten. Hier ist eine Dienstprogrammmethode, die genau das tut und mit der XOM- Bibliothek implementiert wird :

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

Ich habe getestet, dass es funktioniert, und die Ergebnisse hängen nicht von Ihrer JRE-Version oder Ähnlichem ab. Schauen Sie sich das an, um zu sehen, wie Sie das Ausgabeformat nach Ihren Wünschen anpassen könnenSerializer API an .

Dies kam tatsächlich länger heraus als ich dachte - einige zusätzliche Zeilen wurden benötigt, weil man schreiben Serializermöchte OutputStream. Beachten Sie jedoch, dass hier nur sehr wenig Code für das eigentliche XML-Twiddling vorhanden ist.

(Diese Antwort ist ein Teil meiner Bewertung von XOM, die wurden vorgeschlagen als eine Option in meiner Frage über die beste Java XML - Bibliothek dom4j ersetzen Für die Aufzeichnung mit dom4j Sie dies erreichen könnten mit ähnlicher Leichtigkeit verwenden. XMLWriterUnd OutputFormat. Bearbeiten : .. .Wie in demonstrierte mlo55 Antwort .)

Jonik
quelle
2
Danke, das habe ich gesucht. Wenn Sie ein XML bereits in einem "Dokument" -Objekt mit XOM analysiert haben, können Sie es direkt an serializer.write (Dokument) übergeben.
Thibault D.
12

Kevin Hakanson sagte: "Wenn Sie jedoch wissen, dass Ihre XML-Zeichenfolge gültig ist und Sie nicht den Speicheraufwand für das Parsen einer Zeichenfolge in ein DOM aufbringen möchten, führen Sie eine Transformation über das DOM aus, um eine Zeichenfolge zurückzugewinnen Führen Sie einfach ein altmodisches Zeichen durch Zeichenanalyse durch. Fügen Sie nach jedem Zeichen eine neue Zeile und Leerzeichen ein, behalten Sie den Zähler bei und rücken Sie ihn ein (um die Anzahl der Leerzeichen zu bestimmen), die Sie für jedes <...> erhöhen und für jedes, das Sie sehen, verringern. "

Einverstanden. Ein solcher Ansatz ist viel schneller und weist weitaus weniger Abhängigkeiten auf.

Beispiellösung:

/**
 * 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
quelle
2
So sollte es gemacht werden. Formatieren Sie im laufenden Betrieb auf Zeichenfolgenebene. Dies ist die einzige Lösung, die ungültiges oder unvollständiges XML formatiert.
Florian F
11

Hmmm ... hat so etwas gesehen und es ist ein bekannter Fehler ... füge einfach diese OutputProperty hinzu.

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

Hoffe das hilft ...

Sandeep Phukan
quelle
2
Woher kommt diese OutputPropertiesFactory?
Helenov
import com.sun.org.apache.xml.internal.serializer. *;
Gaurav
9

In Bezug auf den Kommentar "Sie müssen zuerst einen DOM-Baum erstellen": Nein, das müssen und sollten Sie nicht tun.

Erstellen Sie stattdessen eine StreamSource (neue StreamSource (neuer StringReader (str)) und geben Sie diese an den genannten Identitätstransformator weiter. Dieser verwendet den SAX-Parser und das Ergebnis ist viel schneller. Das Erstellen eines Zwischenbaums ist in diesem Fall ein reiner Aufwand. Ansonsten ist die Antwort mit dem höchsten Rang gut.

StaxMan
quelle
1
Ich stimme von ganzem Herzen zu: Das Erstellen des Zwischen-DOM-Baums ist eine Verschwendung von Speicher. Danke für diese Antwort.
Florian F
9

Scala verwenden:

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

Sie können dies auch in Java tun, wenn Sie von der Datei scala-library.jar abhängig sind. Es sieht aus wie das:

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

Das PrettyPrinterObjekt besteht aus zwei Ints, wobei das erste die maximale Zeilenlänge und das zweite der Einrückungsschritt ist.

Synesso
quelle
9

leicht verbesserte Version von 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();
} 
Codeskraps
quelle
wo ist repeatString (stack ++); Methode..?
user1912935
2
privater statischer String repeatString (int stack) {StringBuilder indent = new StringBuilder (); für (int i = 0; i <Stapel; i ++) {indent.append (""); } return indent.toString (); }
Codeskraps
Die Einrückung funktioniert nicht gut an den End-Tags. Sie müssen einen } else if (row.startsWith("</")) {Teil dazu ändern :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

Hier ist eine Lösung, die für mich funktioniert hat (dank eines Kommentars, den @George Hawkins in einer der Antworten gepostet hat):

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

Wenn Sie sicher sind, dass Sie über ein gültiges XML verfügen, ist dieses einfach und vermeidet XML-DOM-Bäume. Vielleicht hat ein paar Fehler, kommentieren Sie, wenn Sie etwas sehen

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
quelle
2
Wo ist die repeatString-Methode?
user1912935
3
privater statischer String repeatString (int stack) {StringBuilder indent = new StringBuilder (); für (int i = 0; i <Stapel; i ++) {indent.append (""); } return indent.toString (); }
Codeskraps
Ja [user1912935], was @codeskraps schrieb, sollte einfach genug sein :)
milosmns
Verkettung mit einem StringBuilder innerhalb einer Schleife: Schlechte Praxis.
James.garriss
@ james.garriss Aber es ist super einfach, in neue Zeilen aufzuteilen. Dies zeigt nur einen einfachen Ansatz ohne DOM-Bäume.
Milosmns
5

Alle oben genannten Lösungen haben bei mir nicht funktioniert, dann fand ich diese http://myshittycode.com/2014/02/10/java-properly-indenting-xml-string/

Der Hinweis ist, Leerzeichen mit XPath zu entfernen

    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
quelle
1
Beachten Sie, dass die Verwendung der Eigenschaft '{ xml.apache.org/xslt } Einrückungsbetrag ' Sie an eine bestimmte Transformatorimplementierung bindet.
Vallismortis
1
Von allen Lösungen hat diese am besten funktioniert. Ich hatte bereits Leerzeichen und neue Zeilen in meinem XML und wollte meinem Projekt keine weiteren Abhängigkeiten hinzufügen. Ich wünschte, ich müsste das XML nicht analysieren, aber na ja.
Fabio
5

Dieser Code unten funktioniert einwandfrei

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);
    }
}
macht tkach
quelle
5

Ich mische sie alle und schreibe ein kleines Programm. Es liest aus der XML-Datei und druckt aus. Geben Sie statt xzy Ihren Dateipfad an.

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

}
user3083990
quelle
4

Nur eine andere Lösung, die für uns funktioniert

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

Verwenden von 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
quelle
3

Als Alternative zu den Antworten von max , codeskraps , David Easley und milosmns , haben einen Blick auf meine leicht, High-Performance - pretty-Drucker Bibliothek: xml-Formatierer

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

Manchmal, wie beim Ausführen von verspotteten SOAP-Diensten direkt aus einer Datei, ist es gut, einen hübschen Drucker zu haben, der auch bereits hübsch gedrucktes XML verarbeitet:

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

Wie einige kommentiert haben, ist das hübsche Drucken nur eine Möglichkeit, XML in einer besser lesbaren Form darzustellen - Leerzeichen gehören streng genommen nicht in Ihre XML-Daten.

Die Bibliothek ist für das hübsche Drucken zu Protokollierungszwecken vorgesehen und enthält auch Funktionen zum Filtern (Entfernen / Anonymisieren von Teilbäumen) und zum hübschen Drucken von XML in CDATA- und Textknoten.

ThomasRS
quelle
2

Ich hatte das gleiche Problem und habe großen Erfolg mit JTidy ( http://jtidy.sourceforge.net/index.html ).

Beispiel:

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

Unterstrich-Java hat statische Methode U.formatXml(string). Ich bin der Betreuer des Projekts. Live Beispiel

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

Ausgabe:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <tag>
      <nested>hello</nested>
   </tag>
</root>
Valentyn Kolesnikov
quelle
Das ist fantastisch!
senyor
1

Es gibt ein sehr nettes Befehlszeilen-XML-Dienstprogramm namens xmlstarlet ( http://xmlstar.sourceforge.net/). ), das viele Dinge tun kann, die viele Leute benutzen.

Sie können dieses Programm programmgesteuert mit Runtime.exec ausführen und dann die formatierte Ausgabedatei einlesen. Es bietet mehr Optionen und eine bessere Fehlerberichterstattung, als einige Zeilen Java-Code bieten können.

Laden Sie das xmlstarlet herunter: http://sourceforge.net/project/showfiles.php?group_id=66612&package_id=64589

anjanb
quelle
1

Ich habe festgestellt, dass sich in Java 1.6.0_32 die normale Methode zum hübschen Drucken einer XML- Zeichenfolge (unter Verwendung eines Transformer mit einer Null oder einer Identität xslt) nicht so verhält, wie ich es gerne hätte, wenn Tags nur durch Leerzeichen getrennt würden, anstatt keine Trennung zu haben Text. Ich habe versucht, <xsl:strip-space elements="*"/>in meiner Vorlage ohne Erfolg zu verwenden. Die einfachste Lösung, die ich gefunden habe, bestand darin, den Speicherplatz mithilfe eines SAXSource- und XML-Filters so zu entfernen, wie ich es wollte. Da meine Lösung die Protokollierung war, habe ich diese auch erweitert, um mit unvollständigen XML-Fragmenten zu arbeiten. Beachten Sie, dass die normale Methode gut zu funktionieren scheint, wenn Sie eine DOMSource verwenden, aber ich wollte diese wegen der Unvollständigkeit und des Speicheraufwands nicht verwenden.

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

Die Lösungen, die ich hier für Java 1.6+ gefunden habe, formatieren den Code nicht neu, wenn er bereits formatiert ist. Das, was für mich funktioniert hat (und bereits formatierten Code neu formatiert hat), war das Folgende.

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

Es ist ein gutes Werkzeug, das Sie in Ihren Komponententests für den XML-Vergleich mit vollständigen Zeichenfolgen verwenden können.

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

Für diejenigen, die nach einer schnellen und schmutzigen Lösung suchen - für die XML nicht zu 100% gültig sein muss. zB bei REST / SOAP-Protokollierung (man weiß nie, was die anderen senden ;-))

Ich habe einen Code gefunden und weiterentwickelt, den ich online gefunden habe und der meiner Meinung nach hier noch als gültiger möglicher Ansatz fehlt:

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

Hier ist die Ausgabe:

<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>
max
quelle
1

Ich habe eine Antwort mit gesehen Scala, also hier eine andere Groovyfür den Fall, dass jemand sie interessant findet. Der Standardeinzug ist 2 Schritte. Dem XmlNodePrinterKonstruktor kann auch ein anderer Wert übergeben werden.

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

Verwendung von Java, wenn sich groovy jar im Klassenpfad befindet

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

Falls Sie nicht so viel Einrückung benötigen, sondern nur ein paar Zeilenumbrüche, kann es ausreichen, einfach eine Regex zu erstellen ...

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

Der Code ist nett, nicht das Ergebnis wegen fehlender Einrückung.


(Lösungen mit Einrückung finden Sie in anderen Antworten.)

Comonad
quelle
1
Hmmmm ... Ich denke nur laut, wer würde eine solche Lösung brauchen? Der einzige Bereich, den ich sehen kann, sind Daten, die wir von einigen Webdiensten erhalten. Um diese Daten und ihre Gültigkeit zu testen, benötigen Entwickler oder Tester möglicherweise so einfache Daten. Ansonsten keine gute Option ....
Sudhakar Chavali
1
@ SudhakarChavali Ich bin ein Entwickler. Ich brauche das möglicherweise für schmutzige println () - und log.debug () -Hacks. dh manchmal kann ich nur Protokolldateien aus einer eingeschränkten Serverumgebung (mit Webadministrationsoberfläche anstelle von Shell-Zugriff) verwenden, anstatt das Programm Schritt für Schritt zu debuggen.
Comonad