Come stampare abbastanza XML da Java?


443

Ho una stringa Java che contiene XML, senza feed di riga o rientri. Vorrei trasformarlo in una stringa con XML ben formattato. Come faccio a fare questo?

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

Nota: il mio input è una stringa . Il mio output è una stringa .

Risultato (di base) falso:

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

controllare questa domanda: stackoverflow.com/questions/1264849/...
DFA

10
Solo curioso, stai inviando questo output a un file XML o qualcos'altro in cui il rientro conta davvero? Qualche tempo fa ero molto preoccupato di formattare il mio XML per visualizzarlo correttamente ... ma dopo aver trascorso un sacco di tempo su questo mi sono reso conto che dovevo inviare il mio output a un browser Web ea qualsiasi browser Web relativamente moderno visualizzerò effettivamente l'XML in una bella struttura ad albero, quindi potrei dimenticare questo problema e andare avanti. Sto citando questo nel caso in cui tu (o altri utenti con lo stesso problema) avessi trascurato gli stessi dettagli.
Abel Morelos,

3
@Abel, salvataggio in file di testo, inserimento in textareas HTML e dumping sulla console per scopi di debug.
Steve McLeod,

2
"messo in attesa come troppo ampio" - è difficile essere più precisi di quanto lo sia attualmente la domanda!
Steve McLeod,

Risposte:


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

Nota: i risultati possono variare in base alla versione di Java. Cerca soluzioni alternative specifiche per la tua piattaforma.


1
Come fare in modo che l'output non contenga <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham,

19
Per omettere la <?xml ...>dichiarazione, aggiungeretransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
rustyx il

4
I lettori occasionali potrebbero trovare utile una versione migliorata della soluzione qui descritta ( stackoverflow.com/a/33541820/363573 ).
Stephan,

5
dove è docdefinito?
Florian F,

6
Questo non risponde alla mia domanda: come posso formattare una stringa che contiene XML? Questa risposta presuppone già che tu abbia in qualche modo convertito l'oggetto String in un altro oggetto.
Steve McLeod,

136

Ecco una risposta alla mia domanda. Ho combinato le risposte dei vari risultati per scrivere una classe che stampa piuttosto XML.

Nessuna garanzia su come risponde con XML non valido o documenti di grandi dimensioni.

package ecb.sdw.pretty;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;

/**
 * Pretty-prints xml, supplied as a string.
 * <p/>
 * eg.
 * <code>
 * String formattedXml = new XmlFormatter().format("<tag><nested>hello</nested></tag>");
 * </code>
 */
public class XmlFormatter {

    public XmlFormatter() {
    }

    public String format(String unformattedXml) {
        try {
            final Document document = parseXmlFile(unformattedXml);

            OutputFormat format = new OutputFormat(document);
            format.setLineWidth(65);
            format.setIndenting(true);
            format.setIndent(2);
            Writer out = new StringWriter();
            XMLSerializer serializer = new XMLSerializer(out, format);
            serializer.serialize(document);

            return out.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private Document parseXmlFile(String in) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            InputSource is = new InputSource(new StringReader(in));
            return db.parse(is);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        String unformattedXml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?><QueryMessage\n" +
                        "        xmlns=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message\"\n" +
                        "        xmlns:query=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/query\">\n" +
                        "    <Query>\n" +
                        "        <query:CategorySchemeWhere>\n" +
                        "   \t\t\t\t\t         <query:AgencyID>ECB\n\n\n\n</query:AgencyID>\n" +
                        "        </query:CategorySchemeWhere>\n" +
                        "    </Query>\n\n\n\n\n" +
                        "</QueryMessage>";

        System.out.println(new XmlFormatter().format(unformattedXml));
    }

}

13
Solo per notare che questa risposta richiede l'uso di Xerces. Se non vuoi aggiungere questa dipendenza, puoi semplicemente usare le librerie jdk standard e javax.xml.transform.Transformer (vedi la mia risposta sotto)
khylo

45
Nel 2008 questa era una buona risposta, ma ora tutto ciò può essere fatto con le classi JDK standard anziché con le classi Apache. Vedi xerces.apache.org/xerces2-j/faq-general.html#faq-6 . Sì, questa è una FAQ di Xerces ma la risposta riguarda le classi JDK standard. L'implementazione 1.5 iniziale di queste classi ha avuto molti problemi, ma tutto funziona bene dall'1.6 in poi. Copia l'esempio di LSSerializer nelle FAQ, trita il bit "..." e aggiungi writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);dopo la LSSerializer writer = ...riga.
George Hawkins,

2
Ho creato una piccola classe usando l'esempio di Apache, a cui @GeorgeHawkins ha dato un link. Manca il modo in cui la variabile è documentstata inizializzata, quindi ho pensato di poter aggiungere la decelerazione e farne un rapido esempio. Fammi sapere se dovrei cambiare qualcosa, pastebin.com/XL7932aC
samwell

non è vero che puoi farlo solo con jdk. almeno non in modo affidabile. dipende da alcune implementazioni del registro interno che non sono attive con il mio jdk7u72 per impostazione predefinita. quindi è ancora meglio usare direttamente le cose di Apache.
user1050755

Ecco una soluzione senza dipendenze: stackoverflow.com/a/33541820/363573 .
Stephan,

131

una soluzione più semplice basata su questa risposta :

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

TestCase:

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

ritorna:

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

1
Questo è il codice che ho sempre usato, ma in questa azienda non ha funzionato, suppongo che stiano usando un'altra libreria di trasformazione XML. Ho creato la fabbrica come una linea separata e poi l'ho fatto factory.setAttribute("indent-number", 4);e ora funziona.
Adrian Smith,

Come fare in modo che l'output non contenga <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham,

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

5
Ciao, sto usando questo codice esatto, e il mio formato correttamente con l'eccezione del primo elemento Quindi, questo: <?xml version="1.0" encoding="UTF-8"?><root>è tutto su una riga. Qualche idea sul perché?
CodyK,

2
@Codemiester: sembra essere un bug (vedi stackoverflow.com/a/18251901/3375325 ). L'aggiunta ha transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");funzionato per me.
Janansohn,

100

Ora è il 2012 e Java può fare più di quanto non facesse con XML, vorrei aggiungere un'alternativa alla mia risposta accettata. Questo non ha dipendenze al di fuori di 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));
    }
}

Nessuna indentazione, ma funziona con questo: System.setProperty (DOMImplementationRegistry.PROPERTY, "com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");
ggb667,

1
Come si aggiunge il rientro a questo esempio?
ggb667,

2
@DanTemple Sembra che tu abbia bisogno di usare LSOutput per controllare la codifica. Vedi chipkillmar.net/2009/03/25/pretty-print-xml-from-a-dom
Joshua Davis

1
Ho provato ad usarlo in Andriod ma non sono riuscito a trovare il pacchetto `DOMImplementationRegistry. Sto usando java 8.
Chintan Soni,

2
grazie per aver incluso anche l'elenco di importazione, tanti pacchetti in conflitto disponibili per dare un senso alla combinazione necessaria altrimenti ..
Leon

54

Solo per notare che la risposta più votata richiede l'uso di xerces.

Se non vuoi aggiungere questa dipendenza esterna, puoi semplicemente usare le librerie jdk standard (che in realtà sono costruite usando xerces internamente).

NB Si è verificato un errore con la versione 1.5 di jdk, consultare http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446 ma ora è stato risolto.,

(Nota se si verifica un errore questo restituirà il testo originale)

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

}

In questo caso le schede a sinistra non vengono utilizzate. Tutti i tag iniziano al primo simbolo della riga, come al solito testo.
Ruslan,

non è necessario specificare un set di caratteri per la conversione avanti e indietro tra byte e stringa?
Will Glass,

2
Non dovrebbe essere necessario convertire da e in byte array / String. Per lo meno dovresti specificare charset quando lo fai. Un'opzione migliore sarebbe quella di utilizzare le classi StringReader e StringWriter racchiuse in InputSource e StreamResult.
Maximdim,

non funziona. devi giocare con qualche implementazione del registro interno.
user1050755

Ecco una variante più semplice di questa soluzione: stackoverflow.com/a/33541820/363573
Stephan,

32

Ho stampato piuttosto in passato usando il metodo org.dom4j.io.OutputFormat.createPrettyPrint ()

public String prettyPrint(final String xml){  

    if (StringUtils.isBlank(xml)) {
        throw new RuntimeException("xml was null or blank in prettyPrint()");
    }

    final StringWriter sw;

    try {
        final OutputFormat format = OutputFormat.createPrettyPrint();
        final org.dom4j.Document document = DocumentHelper.parseText(xml);
        sw = new StringWriter();
        final XMLWriter writer = new XMLWriter(sw, format);
        writer.write(document);
    }
    catch (Exception e) {
        throw new RuntimeException("Error pretty printing xml:\n" + xml, e);
    }
    return sw.toString();
}

3
La soluzione accettata non rientra correttamente nei tag nidificati nel mio caso, questo lo fa.
Chase Seibert,

3
L'ho usato in combinazione con la rimozione di tutti gli spazi finali alla fine delle righe:prettyPrintedString.replaceAll("\\s+\n", "\n")
jediz,

19

Ecco un modo per farlo usando dom4j :

importazioni:

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

Codice:

String xml = "<your xml='here'/>";  
Document doc = DocumentHelper.parseText(xml);  
StringWriter sw = new StringWriter();  
OutputFormat format = OutputFormat.createPrettyPrint();  
XMLWriter xw = new XMLWriter(sw, format);  
xw.write(doc);  
String result = sw.toString();

1
Questo non ha funzionato per me. Ha appena dato qualcosa di simile: <?xml version...su una riga e tutto il resto su un'altra riga.
sixtyfootersdude,

14

Dato che stai iniziando con a String, devi convertire un DOMoggetto (ad es. Node) Prima di poter usare Transformer. Tuttavia, se sai che la tua stringa XML è valida e non vuoi incorrere nel sovraccarico di memoria dell'analisi di una stringa in un DOM, quindi esegui una trasformazione sul DOM per recuperare una stringa - potresti semplicemente fare un vecchio stile carattere per analisi del personaggio. Inserisci una nuova riga e spazi dopo ogni </...>carattere, mantieni e rientra il contatore (per determinare il numero di spazi) che incrementi per ogni <...>e diminuisci per ogni </...>cosa che vedi.

Disclaimer - Ho fatto una modifica taglia / incolla / testo delle funzioni seguenti, quindi potrebbero non essere compilate così come sono.

public static final Element createDOM(String strXML) 
    throws ParserConfigurationException, SAXException, IOException {

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setValidating(true);
    DocumentBuilder db = dbf.newDocumentBuilder();
    InputSource sourceXML = new InputSource(new StringReader(strXML));
    Document xmlDoc = db.parse(sourceXML);
    Element e = xmlDoc.getDocumentElement();
    e.normalize();
    return e;
}

public static final void prettyPrint(Node xml, OutputStream out)
    throws TransformerConfigurationException, TransformerFactoryConfigurationError, TransformerException {
    Transformer tf = TransformerFactory.newInstance().newTransformer();
    tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    tf.setOutputProperty(OutputKeys.INDENT, "yes");
    tf.transform(new DOMSource(xml), new StreamResult(out));
}

1
"Tuttavia, se sai che la tua stringa XML è valida ..." buon punto. Vedi la mia soluzione basata su questo approccio di seguito.
David Easley,

12

Se l'utilizzo di una libreria XML di terze parti è ok, puoi cavartela con qualcosa di significativamente più semplice di quello che suggeriscono le risposte attualmente più votate .

È stato affermato che sia l'input che l'output dovrebbero essere stringhe, quindi ecco un metodo di utilità che fa proprio questo, implementato con la libreria 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");
}

Ho provato che funziona e i risultati non dipendono dalla tua versione di JRE o qualcosa del genere. Per vedere come personalizzare il formato di output a proprio piacimento, dai un'occhiata Serializerall'API.

Questo in realtà è uscito più a lungo di quanto pensassi - erano necessarie alcune righe extra perché Serializervuole OutputStreamscrivere. Ma nota che qui c'è pochissimo codice per l'effettivo twiddling XML.

(Questa risposta fa parte della mia valutazione di XOM, che è stata suggerita come un'opzione nella mia domanda sulla migliore libreria XML Java per sostituire dom4j. Per la cronaca, con dom4j è possibile ottenere questo con facilità simile usando XMLWritere OutputFormat. Modifica : .. .come dimostrato nella risposta di mlo55 .)


2
Grazie, è quello che stavo cercando. Se hai già analizzato un XML con XOM in un oggetto "Document", puoi passarlo direttamente a serializer.write (documento);
Thibault D.

12

Kevin Hakanson ha dichiarato: "Tuttavia, se sai che la tua stringa XML è valida e non vuoi incorrere nel sovraccarico di memoria dell'analisi di una stringa in un DOM, quindi eseguendo una trasformazione sul DOM per ottenere una stringa indietro, potresti fai solo un po 'di vecchio stile con l'analisi dei caratteri. Inserisci una nuova riga e spazi dopo ogni carattere, mantieni e indenta il contatore (per determinare il numero di spazi) che incrementi per ogni <...> e decrementa per ogni cosa che vedi. "

Concordato. Un tale approccio è molto più veloce e ha molte meno dipendenze.

Soluzione di esempio:

/**
 * XML utils, including formatting.
 */
public class XmlUtils
{
  private static XmlFormatter formatter = new XmlFormatter(2, 80);

  public static String formatXml(String s)
  {
    return formatter.format(s, 0);
  }

  public static String formatXml(String s, int initialIndent)
  {
    return formatter.format(s, initialIndent);
  }

  private static class XmlFormatter
  {
    private int indentNumChars;
    private int lineLength;
    private boolean singleLine;

    public XmlFormatter(int indentNumChars, int lineLength)
    {
      this.indentNumChars = indentNumChars;
      this.lineLength = lineLength;
    }

    public synchronized String format(String s, int initialIndent)
    {
      int indent = initialIndent;
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < s.length(); i++)
      {
        char currentChar = s.charAt(i);
        if (currentChar == '<')
        {
          char nextChar = s.charAt(i + 1);
          if (nextChar == '/')
            indent -= indentNumChars;
          if (!singleLine)   // Don't indent before closing element if we're creating opening and closing elements on a single line.
            sb.append(buildWhitespace(indent));
          if (nextChar != '?' && nextChar != '!' && nextChar != '/')
            indent += indentNumChars;
          singleLine = false;  // Reset flag.
        }
        sb.append(currentChar);
        if (currentChar == '>')
        {
          if (s.charAt(i - 1) == '/')
          {
            indent -= indentNumChars;
            sb.append("\n");
          }
          else
          {
            int nextStartElementPos = s.indexOf('<', i);
            if (nextStartElementPos > i + 1)
            {
              String textBetweenElements = s.substring(i + 1, nextStartElementPos);

              // If the space between elements is solely newlines, let them through to preserve additional newlines in source document.
              if (textBetweenElements.replaceAll("\n", "").length() == 0)
              {
                sb.append(textBetweenElements + "\n");
              }
              // Put tags and text on a single line if the text is short.
              else if (textBetweenElements.length() <= lineLength * 0.5)
              {
                sb.append(textBetweenElements);
                singleLine = true;
              }
              // For larger amounts of text, wrap lines to a maximum line length.
              else
              {
                sb.append("\n" + lineWrap(textBetweenElements, lineLength, indent, null) + "\n");
              }
              i = nextStartElementPos - 1;
            }
            else
            {
              sb.append("\n");
            }
          }
        }
      }
      return sb.toString();
    }
  }

  private static String buildWhitespace(int numChars)
  {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < numChars; i++)
      sb.append(" ");
    return sb.toString();
  }

  /**
   * Wraps the supplied text to the specified line length.
   * @lineLength the maximum length of each line in the returned string (not including indent if specified).
   * @indent optional number of whitespace characters to prepend to each line before the text.
   * @linePrefix optional string to append to the indent (before the text).
   * @returns the supplied text wrapped so that no line exceeds the specified line length + indent, optionally with
   * indent and prefix applied to each line.
   */
  private static String lineWrap(String s, int lineLength, Integer indent, String linePrefix)
  {
    if (s == null)
      return null;

    StringBuilder sb = new StringBuilder();
    int lineStartPos = 0;
    int lineEndPos;
    boolean firstLine = true;
    while(lineStartPos < s.length())
    {
      if (!firstLine)
        sb.append("\n");
      else
        firstLine = false;

      if (lineStartPos + lineLength > s.length())
        lineEndPos = s.length() - 1;
      else
      {
        lineEndPos = lineStartPos + lineLength - 1;
        while (lineEndPos > lineStartPos && (s.charAt(lineEndPos) != ' ' && s.charAt(lineEndPos) != '\t'))
          lineEndPos--;
      }
      sb.append(buildWhitespace(indent));
      if (linePrefix != null)
        sb.append(linePrefix);

      sb.append(s.substring(lineStartPos, lineEndPos + 1));
      lineStartPos = lineEndPos + 1;
    }
    return sb.toString();
  }

  // other utils removed for brevity
}

2
Questo è il modo in cui dovrebbe essere fatto. Formatta al volo a livello di stringa. Questa è l'unica soluzione che formatterà XML non valido o incompleto.
Florian F,

11

Hmmm ... affrontato qualcosa del genere ed è un bug noto ... basta aggiungere questo OutputProperty ..

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

Spero che sia di aiuto ...


2
Da dove proviene questo OutputPropertiesFactory?
Helenov,

import com.sun.org.apache.xml.internal.serializer. *;
gaurav

9

Riguardo al commento che "devi prima costruire un albero DOM": No, non devi e non dovresti farlo.

Invece, crea uno StreamSource (nuovo StreamSource (nuovo StringReader (str)) e invialo al trasformatore di identità menzionato. Questo utilizzerà il parser SAX e il risultato sarà molto più veloce. In questo caso la creazione di un albero intermedio è pura. Altrimenti la risposta migliore è buona.


1
Sono pienamente d'accordo: costruire l'albero DOM intermedio è uno spreco di memoria. Grazie per quella risposta.
Florian F,

9

Usando scala:

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

Puoi farlo anche in Java, se dipendi da scala-library.jar. Sembra così:

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

L' PrettyPrinteroggetto è costruito con due ints, il primo è la lunghezza massima della linea e il secondo è il passo di rientro.


9

versione leggermente migliorata di 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();
} 

dove è repeatString (stack ++); metodo..?
user1912935

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

Il rientro non funziona bene ai tag di fine. Devi cambiare } else if (row.startsWith("</")) {parte a questo: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

Solo per riferimento futuro, ecco una soluzione che ha funzionato per me (grazie a un commento che @ George Hawkins ha pubblicato in una delle risposte):

DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
LSSerializer writer = impl.createLSSerializer();
writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);
LSOutput output = impl.createLSOutput();
ByteArrayOutputStream out = new ByteArrayOutputStream();
output.setByteStream(out);
writer.write(document, output);
String xmlStr = new String(out.toByteArray());

6

Se sei sicuro di avere un XML valido, questo è semplice ed evita gli alberi DOM XML. Forse ha alcuni bug, commenta se vedi qualcosa

public String prettyPrint(String xml) {
            if (xml == null || xml.trim().length() == 0) return "";

            int stack = 0;
            StringBuilder pretty = new StringBuilder();
            String[] rows = xml.trim().replaceAll(">", ">\n").replaceAll("<", "\n<").split("\n");

            for (int i = 0; i < rows.length; i++) {
                    if (rows[i] == null || rows[i].trim().length() == 0) continue;

                    String row = rows[i].trim();
                    if (row.startsWith("<?")) {
                            // xml version tag
                            pretty.append(row + "\n");
                    } else if (row.startsWith("</")) {
                            // closing tag
                            String indent = repeatString("    ", --stack);
                            pretty.append(indent + row + "\n");
                    } else if (row.startsWith("<")) {
                            // starting tag
                            String indent = repeatString("    ", stack++);
                            pretty.append(indent + row + "\n");
                    } else {
                            // tag data
                            String indent = repeatString("    ", stack);
                            pretty.append(indent + row + "\n");
                    }
            }

            return pretty.toString().trim();
    }

2
dov'è il metodo repeatString ..?
user1912935

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

Sì [user1912935], cosa ha scritto @codeskraps, dovrebbe essere abbastanza semplice :)
milosmns,

Concatenazione con StringBuilder all'interno di un loop: cattiva pratica.
james.garriss,

@ james.garriss Ma è semplicissimo suddividere in nuove linee, questo dimostra semplicemente un approccio semplice senza alberi DOM.
milosmns,

5

Tutte le soluzioni sopra non hanno funzionato per me, quindi ho trovato questo http://myshittycode.com/2014/02/10/java-properly-indenting-xml-string/

L'indizio è rimuovere gli spazi bianchi con XPath

    String xml = "<root>" +
             "\n   " +
             "\n<name>Coco Puff</name>" +
             "\n        <total>10</total>    </root>";

try {
    Document document = DocumentBuilderFactory.newInstance()
            .newDocumentBuilder()
            .parse(new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8"))));

    XPath xPath = XPathFactory.newInstance().newXPath();
    NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']",
                                                  document,
                                                  XPathConstants.NODESET);

    for (int i = 0; i < nodeList.getLength(); ++i) {
        Node node = nodeList.item(i);
        node.getParentNode().removeChild(node);
    }

    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

    StringWriter stringWriter = new StringWriter();
    StreamResult streamResult = new StreamResult(stringWriter);

    transformer.transform(new DOMSource(document), streamResult);

    System.out.println(stringWriter.toString());
}
catch (Exception e) {
    e.printStackTrace();
}

1
Si noti che l'uso della proprietà '{ xml.apache.org/xslt } indent-amount' ti legherà a un'implementazione specifica del trasformatore.
vallismortis,

1
Da tutte le soluzioni questa ha funzionato al meglio. Avevo già spazi e nuove linee nel mio XML e non volevo aggiungere più dipendenze al mio progetto. Vorrei non dover analizzare l'XML ma vabbè.
Fabio

5

Questo codice qui sotto funziona perfettamente

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

String formattedXml1 = prettyFormat("<root><child>aaa</child><child/></root>");

public static String prettyFormat(String input) {
    return prettyFormat(input, "2");
}

public static String prettyFormat(String input, String indent) {
    Source xmlInput = new StreamSource(new StringReader(input));
    StringWriter stringWriter = new StringWriter();
    try {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", indent);
        transformer.transform(xmlInput, new StreamResult(stringWriter));

        String pretty = stringWriter.toString();
        pretty = pretty.replace("\r\n", "\n");
        return pretty;              
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

5

Li mescolo tutti e scrivo un piccolo programma. Sta leggendo dal file xml e sta stampando. Basta invece di xzy indicare il percorso del file.

    public static void main(String[] args) throws Exception {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setValidating(false);
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(new FileInputStream(new File("C:/Users/xyz.xml")));
    prettyPrint(doc);

}

private static String prettyPrint(Document document)
        throws TransformerException {
    TransformerFactory transformerFactory = TransformerFactory
            .newInstance();
    Transformer transformer = transformerFactory.newTransformer();
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
    DOMSource source = new DOMSource(document);
    StringWriter strWriter = new StringWriter();
    StreamResult result = new StreamResult(strWriter);transformer.transform(source, result);
    System.out.println(strWriter.getBuffer().toString());

    return strWriter.getBuffer().toString();

}

4

Solo un'altra soluzione che funziona per noi

import java.io.StringWriter;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

**
 * Pretty Print XML String
 * 
 * @param inputXmlString
 * @return
 */
public static String prettyPrintXml(String xml) {

    final StringWriter sw;

    try {
        final OutputFormat format = OutputFormat.createPrettyPrint();
        final org.dom4j.Document document = DocumentHelper.parseText(xml);
        sw = new StringWriter();
        final XMLWriter writer = new XMLWriter(sw, format);
        writer.write(document);
    }
    catch (Exception e) {
        throw new RuntimeException("Error pretty printing xml:\n" + xml, e);
    }
    return sw.toString();
}

3

Utilizzando jdom2: http://www.jdom.org/

import java.io.StringReader;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

String prettyXml = new XMLOutputter(Format.getPrettyFormat()).
                         outputString(new SAXBuilder().build(new StringReader(uglyXml)));

3

In alternativa alle risposte di max , codeskraps , David Easley e milosmns , dai un'occhiata alla mia libreria per stampanti piuttosto leggere e ad alte prestazioni: 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
}

A volte, come quando si eseguono servizi SOAP derisi direttamente dal file, è bene avere una stampante carina che gestisca anche XML già abbastanza stampati:

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

Come alcuni hanno commentato, pretty-printing è solo un modo per presentare XML in una forma più leggibile dall'uomo - gli spazi bianchi non appartengono strettamente ai dati XML.

La libreria è progettata per la bella stampa per scopi di registrazione e include anche funzioni per il filtraggio (rimozione di sottostruttura / anonimizzazione) e la bella stampa di XML in nodi CDATA e di testo.


2

Ho avuto lo stesso problema e sto riscuotendo un grande successo con JTidy ( http://jtidy.sourceforge.net/index.html )

Esempio:

Tidy t = new Tidy();
t.setIndentContent(true);
Document d = t.parseDOM(
    new ByteArrayInputStream("HTML goes here", null);

OutputStream out = new ByteArrayOutputStream();
t.pprint(d, out);
String html = out.toString();

2

Underscore-java ha un metodo statico U.formatXml(string). Sono il manutentore del progetto. Esempio dal vivo

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

Produzione:

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

Questo è bellissimo!
senyor

1

esiste una utilissima utility xml a riga di comando chiamata xmlstarlet ( http://xmlstar.sourceforge.net/ ) che può fare molte cose che molte persone usano.

È possibile eseguire questo programma in modo programmatico utilizzando Runtime.exec e quindi leggere il file di output formattato. Ha più opzioni e una migliore segnalazione degli errori di quante poche righe di codice Java possano fornire.

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


1

Ho scoperto che in Java 1.6.0_32 il normale metodo per stampare una stringa XML (usando un Transformer con un null o xslt identità) non si comporta come vorrei se i tag fossero semplicemente separati da spazi bianchi, invece di non avere alcuna separazione testo. Ho provato a utilizzare <xsl:strip-space elements="*"/>nel mio modello senza alcun risultato. La soluzione più semplice che ho trovato è stata quella di eliminare lo spazio nel modo che desideravo utilizzando un filtro SAXSource e XML. Dal momento che la mia soluzione era per la registrazione, ho anche esteso questo per funzionare con frammenti XML incompleti. Nota che il metodo normale sembra funzionare bene se usi un DOMSource ma non volevo usarlo a causa dell'incompletezza e dell'overhead della memoria.

public static class WhitespaceIgnoreFilter extends XMLFilterImpl
{

    @Override
    public void ignorableWhitespace(char[] arg0,
                                    int arg1,
                                    int arg2) throws SAXException
    {
        //Ignore it then...
    }

    @Override
    public void characters( char[] ch,
                            int start,
                            int length) throws SAXException
    {
        if (!new String(ch, start, length).trim().equals("")) 
               super.characters(ch, start, length); 
    }
}

public static String prettyXML(String logMsg, boolean allowBadlyFormedFragments) throws SAXException, IOException, TransformerException
    {
        TransformerFactory transFactory = TransformerFactory.newInstance();
        transFactory.setAttribute("indent-number", new Integer(2));
        Transformer transformer = transFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        StringWriter out = new StringWriter();
        XMLReader masterParser = SAXHelper.getSAXParser(true);
        XMLFilter parser = new WhitespaceIgnoreFilter();
        parser.setParent(masterParser);

        if(allowBadlyFormedFragments)
        {
            transformer.setErrorListener(new ErrorListener()
            {
                @Override
                public void warning(TransformerException exception) throws TransformerException
                {
                }

                @Override
                public void fatalError(TransformerException exception) throws TransformerException
                {
                }

                @Override
                public void error(TransformerException exception) throws TransformerException
                {
                }
            });
        }

        try
        {
            transformer.transform(new SAXSource(parser, new InputSource(new StringReader(logMsg))), new StreamResult(out));
        }
        catch (TransformerException e)
        {
            if(e.getCause() != null && e.getCause() instanceof SAXParseException)
            {
                if(!allowBadlyFormedFragments || !"XML document structures must start and end within the same entity.".equals(e.getCause().getMessage()))
                {
                    throw e;
                }
            }
            else
            {
                throw e;
            }
        }
        out.flush();
        return out.toString();
    }

1

Le soluzioni che ho trovato qui per Java 1.6+ non riformattano il codice se è già formattato. Quello che ha funzionato per me (e riformattato il codice già formattato) era il seguente.

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

È un buon strumento da utilizzare nei test unitari per il confronto XML a stringa intera.

private void assertXMLEqual(String expected, String actual) throws ParserConfigurationException, IOException, SAXException, CanonicalizationException, InvalidCanonicalizerException, TransformerException, IllegalAccessException, ClassNotFoundException, InstantiationException {
    String canonicalExpected = prettyFormat(toCanonicalXml(expected));
    String canonicalActual = prettyFormat(toCanonicalXml(actual));
    assertEquals(canonicalExpected, canonicalActual);
}

1

Per coloro che cercano una soluzione rapida e sporca, che non richiede che l'XML sia valido al 100%. ad es. in caso di registrazione REST / SOAP (non si sa mai cosa inviano gli altri ;-))

Ho trovato e avanzato un codice frammentato che ho trovato online, che a mio avviso manca ancora come valido approccio possibile:

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

ecco l'output:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
        <faultcode>soap:Client</faultcode>
        <faultstring>INVALID_MESSAGE</faultstring>
        <detail>
            <ns3:XcbSoapFault xmlns="" xmlns:ns3="http://www.someapp.eu/xcb/types/xcb/v1">
                <CauseCode>20007</CauseCode>
                <CauseText>INVALID_MESSAGE</CauseText>
                <DebugInfo>Problems creating SAAJ object model</DebugInfo>
            </ns3:XcbSoapFault>
        </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

1

Ho visto una risposta usando Scala, quindi eccone un'altra Groovy, nel caso in cui qualcuno la trovi interessante. Il rientro predefinito è di 2 passaggi, al XmlNodePrintercostruttore può essere passato anche un altro valore.

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

Utilizzo da Java se il vaso groovy è nel percorso di classe

  String xml = "<tag><nested>hello</nested></tag>";
  StringWriter stringWriter = new StringWriter();
  Node node = new XmlParser().parseText(xml);
  new XmlNodePrinter(new PrintWriter(stringWriter)).print(node);
  System.out.println(stringWriter.toString());

1

Nel caso in cui non sia necessario un rientro così tanto, ma alcune interruzioni di riga, potrebbe essere sufficiente semplicemente regex ...

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

Il codice è carino, non il risultato a causa della mancanza di rientro.


(Per soluzioni con rientro, vedere altre risposte.)


1
Hmmmm ... Sto solo pensando ad alta voce, chi avrebbe bisogno di tale soluzione? L'unica area che posso vedere sono i dati che otteniamo da alcuni servizi web e solo per testarli e la loro validità, lo sviluppatore o il tester potrebbero aver bisogno di quelli così facili. Altrimenti non è una buona opzione ....
Sudhakar Chavali

1
@SudhakarChavali sono uno sviluppatore. potrei averne bisogno per gli hack sporchi di println () e log.debug (); cioè alcune volte posso usare solo i file di registro all'interno di un ambiente server limitato (con interfaccia di amministrazione web invece dell'accesso alla shell) invece di un debug passo-passo del programma.
comonad,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.