Jak ładnie wydrukować XML z Javy?

443

Mam ciąg Java, który zawiera XML, bez żadnych linii i wcięć. Chciałbym przekształcić go w String z ładnie sformatowanym XML. Jak mam to zrobic?

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

Uwaga: Moje dane wejściowe są ciągiem . Mój wynik to ciąg .

(Podstawowy) wynik próbny:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <tag>
    <nested>hello</nested>
  </tag>
</root>
Steve McLeod
źródło
sprawdź to pytanie: stackoverflow.com/questions/1264849/…
dfa
10
Ciekawe, czy wysyłasz te dane wyjściowe do pliku XML lub czegoś innego, w którym naprawdę ważne jest wcięcie? Jakiś czas temu byłem bardzo zaniepokojony formatowaniem mojego XML, aby był poprawnie wyświetlany ... ale po spędzeniu nad tym czasu zdałem sobie sprawę, że muszę wysłać moje dane wyjściowe do przeglądarki internetowej i dowolnej stosunkowo nowoczesnej przeglądarki internetowej wyświetli XML w ładnej strukturze drzewa, abym mógł zapomnieć o tym problemie i przejść dalej. Wspominam o tym na wypadek, gdybyście ty (lub inny użytkownik mający ten sam problem) przeoczył ten sam szczegół.
Abel Morelos,
3
@Abel, zapisywanie do plików tekstowych, wstawianie do obszarów tekstowych HTML i zrzut do konsoli w celu debugowania.
Steve McLeod
2
„wstrzymaj jako zbyt szeroki” - trudno jest być bardziej precyzyjnym niż obecnie pytanie!
Steve McLeod

Odpowiedzi:

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

Uwaga: wyniki mogą się różnić w zależności od wersji Java. Wyszukaj obejścia specyficzne dla Twojej platformy.

Lorenzo Boccaccia
źródło
1
Jak zrobić, aby wyjście nie zawierało <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham,
19
Aby pominąć <?xml ...>deklarację, dodajtransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
rustyx
4
Zwykli czytelnicy mogą znaleźć użyteczną ulepszoną wersję opisanego tutaj rozwiązania ( stackoverflow.com/a/33541820/363573 ).
Stephan
5
gdzie jest doczdefiniowane?
Florian F
6
To nie odpowiada na moje pytanie: jak sformatować ciąg zawierający XML? Ta odpowiedź już zakłada, że ​​w jakiś sposób przekształciłeś obiekt String w inny obiekt.
Steve McLeod,
135

Oto odpowiedź na moje własne pytanie. Połączyłem odpowiedzi z różnych wyników, aby napisać klasę, która ładnie drukuje XML.

Brak gwarancji na to, jak zareaguje przy użyciu nieprawidłowego XML lub dużych dokumentów.

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
źródło
13
Należy zauważyć, że ta odpowiedź wymaga użycia Xerces. Jeśli nie chcesz dodawać tej zależności, możesz po prostu użyć standardowych bibliotek jdk i javax.xml.transform.Transformer (patrz moja odpowiedź poniżej)
khylo
45
W 2008 roku była to dobra odpowiedź, ale teraz można to zrobić za pomocą standardowych klas JDK zamiast klas Apache. Zobacz xerces.apache.org/xerces2-j/faq-general.html#faq-6 . Tak, jest to FAQ Xerces, ale odpowiedź obejmuje standardowe klasy JDK. Początkowa implementacja tych klas w wersji 1.5 miała wiele problemów, ale wszystko działa dobrze od wersji 1.6. Skopiuj przykład LSSerializer z FAQ, posiekaj bit „...” i dodaj writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);po LSSerializer writer = ...wierszu.
George Hawkins
2
Utworzyłem małą klasę na przykładzie podanym przez Apache, do którego @GeorgeHawkins podał link. Brakowało sposobu documentinicjowania zmiennej , więc pomyślałem, że mogę dodać opóźnienie i zrobić z niego szybki przykład. Daj mi znać, jeśli powinienem coś zmienić, pastebin.com/XL7932aC
samwell
nie jest prawdą, że możesz to zrobić tylko za pomocą jdk. przynajmniej niewiarygodnie. zależy to od wewnętrznej implementacji rejestru, która domyślnie nie jest aktywna z moim jdk7u72. więc nadal lepiej korzystać bezpośrednio z apache.
user1050755,
Oto rozwiązanie bez żadnych zależności: stackoverflow.com/a/33541820/363573 .
Stephan
131

prostsze rozwiązanie oparte na tej odpowiedzi :

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

walizka testowa:

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

zwroty:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <child>aaa</child>
  <child/>
</root>
dfa
źródło
1
To jest kod, którego zawsze używałem, ale w tej firmie nie działał, zakładam, że używają innej biblioteki do transformacji XML. Stworzyłem fabrykę jako osobną linię, a potem zrobiłem factory.setAttribute("indent-number", 4);i teraz działa.
Adrian Smith
Jak zrobić, aby wyjście nie zawierało <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham,
4
@Harry:transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
jjmontes,
5
Cześć. Korzystam z tego dokładnego kodu i odpowiednio dobieram formaty, z wyjątkiem pierwszego elementu. Więc: <?xml version="1.0" encoding="UTF-8"?><root>wszystko jest w jednej linii. Jakieś pomysły dlaczego?
CodyK,
2
@Codemiester: Wygląda na błąd (patrz stackoverflow.com/a/18251901/3375325 ). Dodawanie transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");działało dla mnie.
jansohn,
100

Teraz jest 2012, a Java może zrobić więcej niż kiedyś z XML, chciałbym dodać alternatywę do mojej zaakceptowanej odpowiedzi. Nie ma żadnych zależności poza Javą 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
źródło
Bez wcięć, ale działa z tym: System.setProperty (DOMImplementationRegistry.PROPERTY, „com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl”);
ggb667
1
Jak dodać wcięcie do tego przykładu?
ggb667
2
@ DanTemple Wygląda na to, że musisz użyć LSOutput do sterowania kodowaniem. Zobacz chipkillmar.net/2009/03/25/pretty-print-xml-from-a-dom
Joshua Davis
1
Próbowałem użyć tego w Andriod, ale nie mogę znaleźć pakietu `DOMImplementationRegistry. Używam java 8.
Chintan Soni
2
dzięki za dołączenie listy importu, więc dostępnych jest tyle sprzecznych pakietów, aby zrozumieć kombinację potrzebną w innym przypadku.
Leon
54

Wystarczy zauważyć, że najwyżej oceniana odpowiedź wymaga użycia kserografów.

Jeśli nie chcesz dodawać tej zewnętrznej zależności, możesz po prostu użyć standardowych bibliotek jdk (które są budowane wewnętrznie przy użyciu xerces).

Uwaga: Wystąpił błąd w wersji 1.5 jdk, patrz http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446 ale problem został już rozwiązany.,

(Uwaga: jeśli wystąpi błąd, spowoduje to zwrócenie oryginalnego tekstu)

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
źródło
W tym przypadku lewe karty nie są używane. Wszystkie tagi zaczynają się od pierwszego symbolu linii, jak zwykły tekst.
Ruslan
czy nie musisz określać zestawu znaków podczas konwertowania między bajtami i łańcuchem?
Will Glass
2
Nie powinno być potrzeby konwertowania z i na tablice bajtów / ciąg znaków. Przynajmniej musisz przy tym określać zestaw znaków. Lepszą opcją byłoby użycie klas StringReader i StringWriter opakowanych w InputSource i StreamResult.
maximdim
nie działa. musisz zadzierać z implementacją rejestru wewnętrznego.
user1050755,
Oto prostszy wariant tego rozwiązania: stackoverflow.com/a/33541820/363573
Stephan
32

Mam dość drukowane w przeszłości za pomocą org.dom4j.io.OutputFormat.createPrettyPrint () metoda

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
źródło
3
Zaakceptowane rozwiązanie nie wcina poprawnie zagnieżdżonych znaczników w moim przypadku, tak właśnie jest.
Chase Seibert,
3
Użyłem tego w połączeniu z usunięciem wszystkich końcowych spacji na końcu linii:prettyPrintedString.replaceAll("\\s+\n", "\n")
jediz
19

Oto sposób na zrobienie tego przy użyciu dom4j :

Import:

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

Kod:

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
źródło
1
To nie działało dla mnie. Dało to po prostu coś takiego: <?xml version...w jednej linii i wszystko inne w innej linii.
sixtyfootersdude
14

Ponieważ zaczynasz od a String, musisz ukryć się przed DOMobiektem (np. Node) Przed użyciem Transformer. Jeśli jednak wiesz, że łańcuch XML jest prawidłowy i nie chcesz ponosić narzutu pamięci podczas analizowania łańcucha w DOM, a następnie uruchom transformację w DOM, aby odzyskać łańcuch - możesz po prostu zrobić trochę staromodny parsowanie znak po znaku. Wstaw nowy wiersz i spacje po każdym </...>znaku, zachowaj i wcięcie licznika (aby określić liczbę spacji), które zwiększasz dla każdego <...>i zmniejszaj dla każdego</...> , widzisz.

Oświadczenie - Zrobiłem edycję wycinania / wklejania / tekstu dla poniższych funkcji, więc mogą się nie kompilować w obecnej postaci.

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
źródło
1
„Jednak jeśli wiesz, że Twój ciąg XML jest prawidłowy ...”, warto. Zobacz moje rozwiązanie oparte na tym podejściu poniżej.
David Easley
12

Jeśli korzystanie z biblioteki XML innej firmy jest w porządku, możesz uciec od czegoś znacznie prostszego niż to, co obecnie najlepiej głosuje odpowiedzi sugerują .

Stwierdzono, że zarówno dane wejściowe, jak i wyjściowe powinny być ciągami, więc oto metoda narzędziowa, która właśnie to robi, zaimplementowana w bibliotece 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");
}

Testowałem, że to działa, a wyniki nie zależą od twojej wersji JRE ani niczego podobnego. Aby zobaczyć, jak dostosować format wyjściowy do swoich potrzeb, spójrz na SerializerAPI.

To faktycznie wyszło dłużej, niż myślałem - potrzebne były dodatkowe wiersze, ponieważ Serializerchciałbym OutputStreamnapisać do. Zauważ jednak, że jest tu bardzo mało kodu do faktycznego kręcenia XML.

(Ta odpowiedź jest częścią mojej oceny XOM, która została zasugerowana jako jedna z opcji mojego pytania o najlepszą bibliotekę Java XML, która zastąpiłaby dom4j. Dla przypomnienia, z dom4j można to osiągnąć z podobną łatwością przy użyciu XMLWriteri OutputFormat. Edytuj : .. . wykazano w odpowiedzi mlo55 ).

Jonik
źródło
2
Dzięki, tego szukałem. Jeśli masz już XML przetworzony z XOM w obiekcie „Dokument”, możesz przekazać go bezpośrednio do serializer.write (dokument);
Thibault D.,
12

Kevin Hakanson powiedział: „Jeśli jednak wiesz, że łańcuch XML jest prawidłowy i nie chcesz ponosić dodatkowej pamięci związanej z analizowaniem łańcucha w DOM, a następnie uruchamianiem transformacji w DOM, aby odzyskać łańcuch - możesz po prostu parsuj staroświecką postać po analizie znaków. Wstawianie nowego wiersza i spacji po każdym znaku, zachowaj i wcięcie licznika (aby określić liczbę spacji), które zwiększasz dla każdego <...> i zmniejszaj dla każdego, co widzisz. "

Zgoda. Takie podejście jest znacznie szybsze i ma znacznie mniej zależności.

Przykładowe rozwiązanie:

/**
 * 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
źródło
2
Tak właśnie należy to zrobić. Formatuj w locie na poziomie łańcucha. To jedyne rozwiązanie, które sformatuje nieprawidłowy lub niekompletny XML.
Florian F
11

Hmmm ... napotkałem coś takiego i jest to znany błąd ... po prostu dodaj ten OutputProperty ..

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

Mam nadzieję że to pomoże ...

Sandeep Phukan
źródło
2
Skąd pochodzi ten OutputPropertiesFactory?
helenov
import com.sun.org.apache.xml.internal.serializer. *;
gaurav
9

W odniesieniu do komentarza, że ​​„musisz najpierw zbudować drzewo DOM”: Nie, nie musisz i nie powinieneś tego robić.

Zamiast tego utwórz StreamSource (nowy StreamSource (nowy StringReader (str)) i podaj go do wspomnianego transformatora tożsamości. Użyje on analizatora składni SAX, a wynik będzie znacznie szybszy. Budowanie drzewa pośredniego jest w tym przypadku czystym kosztem. W przeciwnym razie odpowiedź na najwyższym miejscu jest dobra.

StaxMan
źródło
1
Z całego serca się zgadzam: budowanie pośredniego drzewa DOM jest marnotrawstwem pamięci. Thansk za tę odpowiedź.
Florian F
9

Za pomocą scala:

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

Możesz to zrobić również w Javie, jeśli zależy Ci na scala-library.jar. To wygląda tak:

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

PrettyPrinterPrzedmiot zbudowany jest z dwóch wskazówki, z których pierwszy jest maksymalna długość wiersza a druga jest etap wgłębieniem.

Synesso
źródło
9

nieco ulepszona wersja z 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();
} 
kodykapsy
źródło
gdzie jest repeatString (stos ++); metoda..?
user1912935
2
prywatny statyczny String repeatString (int stack) {StringBuilder indent = new StringBuilder (); for (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
codkraps
Wcięcie nie działa dobrze na tagach końcowych. Musisz zmienić } else if (row.startsWith("</")) {część na to: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

Dla odniesienia w przyszłości, oto rozwiązanie, które zadziałało dla mnie (dzięki komentarzowi, który @George Hawkins opublikował w jednej z odpowiedzi):

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());
Michał
źródło
6

Jeśli masz pewność, że masz prawidłowy kod XML, ten jest prosty i pozwala uniknąć drzew DOM XML. Może ma jakieś błędy, skomentuj, jeśli coś zobaczysz

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();
    }
milosmny
źródło
2
gdzie jest metoda repeatString ..?
user1912935
3
prywatny statyczny String repeatString (int stack) {StringBuilder indent = new StringBuilder (); for (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
codkraps
Tak [user1912935], co napisał @codeskraps, powinno być dość proste :)
milosmns
Łączenie z StringBuilder wewnątrz pętli: Zła praktyka.
james.garriss
@ james.garriss Ale podział na nowe linie jest bardzo łatwy, ilustruje to proste podejście bez drzew DOM.
milosmns
5

Wszystkie powyższe rozwiązania nie działały dla mnie, a potem to znalazłem http://myshittycode.com/2014/02/10/java-properly-indenting-xml-string/

Wskazówką jest usunięcie białych znaków za pomocą 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
źródło
1
Zauważ, że użycie właściwości „{ xml.apache.org/xslt } wcięcie-ilość” wiąże cię z konkretną implementacją transformatora.
vallismortis
1
Ze wszystkich rozwiązań ten działał najlepiej. Miałem już spacje i nowe wiersze w moim pliku XML oraz nie chciałem dodawać więcej zależności do mojego projektu. Chciałbym nie musiałem analizować XML, ale no cóż.
Fabio,
5

Poniższy kod działa idealnie

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
źródło
5

Łączę je wszystkie i piszę jeden mały program. Odczytuje z pliku xml i drukuje. Po prostu Zamiast xzy podaj ścieżkę do pliku.

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

}
użytkownik3083990
źródło
4

Kolejne rozwiązanie, które działa dla nas

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
źródło
3

Korzystanie z 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
źródło
3

Jako alternatywę dla odpowiedzi od max , codekraps , David Easley i milosmns , spójrz na moją lekką, wydajną bibliotekę ładnych drukarek: 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
}

Czasami, na przykład podczas uruchamiania fałszywych usług SOAP bezpośrednio z pliku, dobrze jest mieć ładną drukarkę, która obsługuje również już ładnie wydrukowane XML:

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

Jak niektórzy komentowali, ładne drukowanie to tylko sposób prezentacji XML w bardziej czytelnej dla człowieka formie - białe znaki ściśle nie należą do twoich danych XML.

Biblioteka jest przeznaczona do ładnego drukowania do celów logowania, a także zawiera funkcje do filtrowania (usuwanie / anonimizacja poddrzewa) i ładnego drukowania XML w węzłach CDATA i Text.

ThomasRS
źródło
2

Miałem ten sam problem i odnoszę wielki sukces z JTidy ( http://jtidy.sourceforge.net/index.html )

Przykład:

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
źródło
2

Underscore-java ma metodę statyczną U.formatXml(string). Jestem opiekunem projektu. Przykład na żywo

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

Wynik:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <tag>
      <nested>hello</nested>
   </tag>
</root>
Valentyn Kolesnikov
źródło
To jest niesamowite!
senyor
1

Odkryłem, że w Javie 1.6.0_32 normalna metoda ładnego drukowania łańcucha XML (przy użyciu transformatora z zerowym lub identyfikatorem xslt) nie zachowuje się tak, jak chciałbym, jeśli znaczniki są tylko oddzielone białymi spacjami, w przeciwieństwie do braku oddzielania tekst. Próbowałem użyć <xsl:strip-space elements="*"/>w swoim szablonie bezskutecznie. Najprostszym rozwiązaniem, jakie znalazłem, było usunięcie przestrzeni tak, jak chciałem, za pomocą filtru SAXSource i XML. Ponieważ moim rozwiązaniem było logowanie, rozszerzyłem to również na niekompletne fragmenty XML. Zauważ, że normalna metoda wydaje się działać dobrze, jeśli używasz DOMSource, ale nie chciałem jej używać z powodu niekompletności i obciążenia pamięci.

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
źródło
1

Rozwiązania, które znalazłem tutaj dla Java 1.6+, nie formatują kodu, jeśli jest już sformatowany. Ten, który zadziałał dla mnie (i ponownie sformatował już sformatowany kod), był następujący.

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

Jest to dobre narzędzie do użycia w testach jednostkowych do pełnego porównania xml.

private void assertXMLEqual(String expected, String actual) throws ParserConfigurationException, IOException, SAXException, CanonicalizationException, InvalidCanonicalizerException, TransformerException, IllegalAccessException, ClassNotFoundException, InstantiationException {
    String canonicalExpected = prettyFormat(toCanonicalXml(expected));
    String canonicalActual = prettyFormat(toCanonicalXml(actual));
    assertEquals(canonicalExpected, canonicalActual);
}
Wojtek
źródło
1

Dla tych, którzy szukają szybkiego i brudnego rozwiązania - które nie musi być w 100% poprawne. np. w przypadku rejestrowania REST / SOAP (nigdy nie wiadomo, co wysyłają inni ;-))

Znalazłem i ulepszyłem wycięty kod, który znalazłem w sieci, ale myślę, że wciąż tutaj go brakuje, ponieważ jest to poprawne możliwe podejście:

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

oto wynik:

<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
źródło
1

Widziałem jedną odpowiedź użyciu Scala, więc tutaj jest jeszcze jeden w Groovy, na wypadek gdyby ktoś znajdzie to ciekawe. Domyślne wcięcie to 2 kroki, XmlNodePrinterkonstruktorowi można również przekazać inną wartość.

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

Użycie z Javy, jeśli groovy jar znajduje się w ścieżce klas

  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
źródło
1

Jeśli nie potrzebujesz tyle wcięć, ale kilku podziałów linii, wystarczy po prostu regexować ...

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

Kod jest fajny, a nie wynik z powodu braku wcięcia.


(W przypadku rozwiązań z wcięciem zobacz inne odpowiedzi.)

comonad
źródło
1
Hmmmm ... Po prostu myślę głośno, kto będzie potrzebował takiego rozwiązania? Jedyny obszar, który widzę, to dane, które otrzymujemy z niektórych usług internetowych. Aby tylko przetestować te dane i ich ważność, programista lub tester mogą potrzebować takich łatwych. W przeciwnym razie nie jest to dobra opcja ....
Sudhakar Chavali
1
@SudhakarChavali Jestem programistą. może potrzebuję tego do brudnych hacków println () i log.debug (); tzn. czasami mogę używać plików dziennika tylko z poziomu ograniczonego środowiska serwera (z interfejsem administratora sieci zamiast dostępu do powłoki) zamiast rozsądnego debugowania programu krok po kroku.
comonad