Comment imprimer du XML à partir de Java?

443

J'ai une chaîne Java qui contient du XML, sans sauts de ligne ni indentations. Je voudrais le transformer en une chaîne avec du XML bien formaté. Comment puis-je faire cela?

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

Remarque: Mon entrée est un chaîne . Ma sortie est une chaîne .

Résultat de simulation (de base):

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <tag>
    <nested>hello</nested>
  </tag>
</root>
Steve McLeod
la source
vérifier cette question: stackoverflow.com/questions/1264849/…
dfa
10
Juste curieux, envoyez-vous cette sortie vers un fichier XML ou quelque chose d'autre où l'indentation compte vraiment? Il y a quelque temps, j'étais très préoccupé par le formatage de mon XML afin de l'afficher correctement ... mais après y avoir passé beaucoup de temps, j'ai réalisé que je devais envoyer ma sortie à un navigateur Web et à tout navigateur Web relativement moderne. affichera le XML dans une belle arborescence, donc je pourrais oublier ce problème et passer à autre chose. Je mentionne cela juste au cas où vous (ou un autre utilisateur ayant le même problème) auriez pu ignorer le même détail.
Abel Morelos
3
@Abel, enregistrement dans des fichiers texte, insertion dans une zone de texte HTML et vidage sur la console à des fins de débogage.
Steve McLeod
2
"mise en attente car trop large" - il est difficile d'être plus précis que la question actuelle!
Steve McLeod

Réponses:

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

Remarque: les résultats peuvent varier selon la version Java. Recherchez des solutions de contournement spécifiques à votre plateforme.

Lorenzo Boccaccia
la source
1
Comment faire pour que la sortie ne contienne pas <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham
19
Pour omettre la <?xml ...>déclaration, ajouteztransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
rustyx
4
Les lecteurs occasionnels peuvent trouver utile une version améliorée de la solution décrite ici ( stackoverflow.com/a/33541820/363573 ).
Stephan
5
où est docdéfini?
Florian F
6
Cela ne répond pas à ma question: comment formater une chaîne contenant du XML? Cette réponse suppose déjà que vous avez en quelque sorte converti l'objet String en un autre objet.
Steve McLeod
136

Voici une réponse à ma propre question. J'ai combiné les réponses des différents résultats pour écrire une classe qui imprime assez XML.

Aucune garantie sur la façon dont il répond avec XML non valide ou des documents volumineux.

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
la source
13
Juste pour noter que cette réponse nécessite l'utilisation de Xerces. Si vous ne voulez pas ajouter cette dépendance, vous pouvez simplement utiliser les bibliothèques jdk standard et javax.xml.transform.Transformer (voir ma réponse ci-dessous)
khylo
45
En 2008, c'était une bonne réponse, mais maintenant tout cela peut être fait avec des classes JDK standard plutôt qu'avec des classes Apache. Voir xerces.apache.org/xerces2-j/faq-general.html#faq-6 . Oui, c'est une FAQ Xerces, mais la réponse couvre les classes JDK standard. L'implémentation initiale de 1.5 de ces classes a eu de nombreux problèmes, mais tout fonctionne bien à partir de 1.6. Copiez l'exemple LSSerializer dans la FAQ, hachez le bit "..." et ajoutez writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE);après la LSSerializer writer = ...ligne.
George Hawkins
2
J'ai créé une petite classe en utilisant l'exemple donné par Apache, auquel @GeorgeHawkins a donné un lien. Il manquait la façon dont la variable a documentété initialisée, j'ai donc pensé pouvoir ajouter la décélération et en faire un exemple rapide. Faites-moi savoir si je dois changer quelque chose, pastebin.com/XL7932aC
samwell
il n'est pas vrai que vous ne puissiez le faire qu'avec jdk. du moins pas de manière fiable. cela dépend d'une implémentation de registre interne qui n'est pas active avec mon jdk7u72 par défaut. il vaut donc mieux utiliser directement les trucs apache.
user1050755
Voici une solution sans aucune dépendance: stackoverflow.com/a/33541820/363573 .
Stephan
131

une solution plus simple basée sur cette réponse :

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

cas de test:

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

Retour:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <child>aaa</child>
  <child/>
</root>
dfa
la source
1
C'est le code que j'ai toujours utilisé, mais dans cette entreprise, cela n'a pas fonctionné, je suppose qu'ils utilisent une autre bibliothèque de transformation XML. J'ai créé l'usine en tant que ligne distincte, puis je l'ai fait factory.setAttribute("indent-number", 4);et maintenant cela fonctionne.
Adrian Smith
Comment faire pour que la sortie ne contienne pas <?xml version="1.0" encoding="UTF-8"?>?
Thang Pham
4
@Harry:transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
jjmontes
5
Salut, j'utilise ce code exact, et le mien est correctement formaté à l'exception du premier élément Donc, ceci: <?xml version="1.0" encoding="UTF-8"?><root>est tout sur une seule ligne. Des idées pourquoi?
CodyK
2
@Codemiester: semble être un bogue (voir stackoverflow.com/a/18251901/3375325 ). L'ajout a transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");fonctionné pour moi.
jansohn
100

Maintenant que c'est 2012 et Java peut faire plus qu'avant avec XML, je voudrais ajouter une alternative à ma réponse acceptée. Cela n'a pas de dépendances en dehors de Java 6.

import org.w3c.dom.Node;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

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

    public String format(String xml) {

        try {
            final InputSource src = new InputSource(new StringReader(xml));
            final Node document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src).getDocumentElement();
            final Boolean keepDeclaration = Boolean.valueOf(xml.startsWith("<?xml"));

        //May need this: System.setProperty(DOMImplementationRegistry.PROPERTY,"com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");


            final DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
            final DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
            final LSSerializer writer = impl.createLSSerializer();

            writer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); // Set this to true if the output needs to be beautified.
            writer.getDomConfig().setParameter("xml-declaration", keepDeclaration); // Set this to true if the declaration is needed to be outputted.

            return writer.writeToString(document);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

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

        System.out.println(new XmlFormatter().format(unformattedXml));
    }
}
Steve McLeod
la source
Pas d'indentation, mais cela fonctionne avec ceci: System.setProperty (DOMImplementationRegistry.PROPERTY, "com.sun.org.apache.xerces.internal.dom.DOMImplementationSourceImpl");
ggb667
1
Comment ajoutez-vous un retrait à cet exemple?
ggb667
2
@DanTemple Il semble que vous ayez besoin d'utiliser LSOutput pour contrôler l'encodage. Voir chipkillmar.net/2009/03/25/pretty-print-xml-from-a-dom
Joshua Davis
1
J'ai essayé de l'utiliser dans Andriod mais je ne trouve pas le package `DOMImplementationRegistry. J'utilise java 8.
Chintan Soni
2
merci d'avoir inclus la liste d'importation aussi, tant de paquets contradictoires disponibles pour donner un sens à la combinaison nécessaire autrement.
Leon
54

Juste pour noter que la réponse la mieux notée nécessite l'utilisation de xerces.

Si vous ne voulez pas ajouter cette dépendance externe, vous pouvez simplement utiliser les bibliothèques jdk standard (qui sont en fait construites en utilisant xerces en interne).

NB Il y avait un bogue avec la version 1.5 de jdk voir http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446 mais il est résolu maintenant.,

(Notez que si une erreur se produit, cela retournera le texte d'origine)

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
la source
Dans ce cas, les onglets de gauche ne sont pas utilisés. Toutes les balises commencent au premier symbole de la ligne, comme le texte habituel.
Ruslan
n'avez-vous pas besoin de spécifier un jeu de caractères lors de la conversion entre octets et chaîne?
Will Glass
2
Il ne devrait pas être nécessaire de convertir depuis et vers des tableaux d'octets / String. À tout le moins, vous devrez spécifier charset lors de cette opération. Une meilleure option serait d'utiliser les classes StringReader et StringWriter enveloppées dans InputSource et StreamResult.
maximdim
Ca ne fonctionne pas. vous devez jouer avec une implémentation de registre interne.
user1050755
Voici une variante plus simple de cette solution: stackoverflow.com/a/33541820/363573
Stephan
32

J'ai assez imprimé dans le passé en utilisant la méthode 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();
}
mlo55
la source
3
La solution acceptée ne met pas correctement en retrait les balises imbriquées dans mon cas, celle-ci le fait.
Chase Seibert
3
J'ai utilisé ceci en conjonction avec la suppression de tous les espaces de fin à la fin des lignes:prettyPrintedString.replaceAll("\\s+\n", "\n")
jediz
19

Voici une façon de le faire en utilisant dom4j :

Importations:

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

Code:

String xml = "<your xml='here'/>";  
Document doc = DocumentHelper.parseText(xml);  
StringWriter sw = new StringWriter();  
OutputFormat format = OutputFormat.createPrettyPrint();  
XMLWriter xw = new XMLWriter(sw, format);  
xw.write(doc);  
String result = sw.toString();
Mark Pope
la source
1
Cela n'a pas fonctionné pour moi. Cela donnait juste quelque chose comme: <?xml version...sur une ligne et tout le reste sur une autre ligne.
sixtyfootersdude
14

Puisque vous commencez par un String, vous devez convertir un DOMobjet (par exemple Node) avant de pouvoir utiliser le Transformer. Cependant, si vous savez que votre chaîne XML est valide et que vous ne voulez pas encourir la surcharge de mémoire pour analyser une chaîne dans un DOM, puis exécuter une transformation sur le DOM pour récupérer une chaîne - vous pouvez simplement faire de l'ancienne analyse par caractère. Insérez une nouvelle ligne et des espaces après chaque </...>caractère, gardez et indentez le compteur (pour déterminer le nombre d'espaces) que vous incrémentez pour chaque <...>et décrémentez pour chaque que </...>vous voyez.

Avertissement - J'ai fait un copier / coller / éditer le texte des fonctions ci-dessous, donc elles peuvent ne pas compiler telles quelles.

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
la source
1
"Cependant, si vous savez que votre chaîne XML est valide ..." bon point. Voir ma solution basée sur cette approche ci-dessous.
David Easley
12

Si l'utilisation d'une bibliothèque XML tierce est correcte, vous pouvez vous en sortir avec quelque chose de beaucoup plus simple que ce que les réponses actuellement les plus votées suggèrent les .

Il a été déclaré que l'entrée et la sortie devraient être des chaînes, voici donc une méthode utilitaire qui fait exactement cela, implémentée avec la bibliothèque 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");
}

J'ai testé que cela fonctionne, et les résultats ne dépendent pas de votre version JRE ou de quelque chose comme ça. Pour voir comment personnaliser le format de sortie à votre guise, jetez un œil à l' SerializerAPI.

Cela est en fait sorti plus longtemps que je ne le pensais - quelques lignes supplémentaires étaient nécessaires parce que Serializerveut OutputStreamécrire. Mais notez qu'il y a très peu de code pour le twiddling XML réel ici.

(Cette réponse fait partie de mon évaluation de XOM, qui a été suggérée comme une option dans ma question sur la meilleure bibliothèque XML Java pour remplacer dom4j. Pour mémoire, avec dom4j, vous pouvez y parvenir avec la même facilité en utilisant XMLWriteret OutputFormat. Modifier : .. . comme démontré dans la réponse de mlo55 .)

Jonik
la source
2
Merci, c'est ce que je cherchais. Si vous avez un XML déjà analysé avec XOM dans un objet "Document", vous pouvez le passer directement à serializer.write (document);
Thibault D.
12

Kevin Hakanson a déclaré: "Cependant, si vous savez que votre chaîne XML est valide et que vous ne voulez pas encourir la surcharge de mémoire pour analyser une chaîne dans un DOM, puis exécuter une transformation sur le DOM pour récupérer une chaîne - vous pourriez il suffit d'analyser les caractères à l'ancienne. Insérez une nouvelle ligne et des espaces après chaque caractère, gardez et indentez le compteur (pour déterminer le nombre d'espaces) que vous augmentez pour chaque <...> et décrémentez pour chaque que vous voyez. "

D'accord. Une telle approche est beaucoup plus rapide et a beaucoup moins de dépendances.

Exemple de solution:

/**
 * 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
la source
2
C'est ainsi que cela doit être fait. Formater à la volée au niveau de la chaîne. Il s'agit de la seule solution qui formatera du XML invalide ou incomplet.
Florian F du
11

Hmmm ... face à quelque chose comme ça et c'est un bug connu ... il suffit d'ajouter ce OutputProperty ..

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

J'espère que cela t'aides ...

Sandeep Phukan
la source
2
D'où vient cette OutputPropertiesFactory?
helenov
import com.sun.org.apache.xml.internal.serializer. *;
gaurav
9

Concernant le commentaire "vous devez d'abord construire une arborescence DOM": Non, vous n'avez pas besoin et ne devez pas le faire.

Au lieu de cela, créez un StreamSource (nouveau StreamSource (nouveau StringReader (str)) et alimentez-le au transformateur d'identité mentionné. Cela utilisera l'analyseur SAX, et le résultat sera beaucoup plus rapide. La construction d'un arbre intermédiaire est une surcharge pure pour ce cas. Sinon, la réponse la mieux classée est bonne.

StaxMan
la source
1
Je suis tout à fait d'accord: la construction de l'arbre DOM intermédiaire est un gaspillage de mémoire. Merci pour cette réponse.
Florian F du
9

Utilisation de scala:

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

Vous pouvez également le faire en Java, si vous dépendez du scala-library.jar. Cela ressemble à ceci:

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' PrettyPrinterobjet est construit avec deux pouces, le premier étant la longueur de ligne maximale et le second étant l'étape d'indentation.

Synesso
la source
9

version légèrement améliorée de milosmns ...

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

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

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

        String row = rows[i].trim();
        if (row.startsWith("<?")) {
            pretty.append(row + "\n");
        } else if (row.startsWith("</")) {
            String indent = repeatString(--stack);
            pretty.append(indent + row + "\n");
        } else if (row.startsWith("<") && row.endsWith("/>") == false) {
            String indent = repeatString(stack++);
            pretty.append(indent + row + "\n");
            if (row.endsWith("]]>")) stack--;
        } else {
            String indent = repeatString(stack);
            pretty.append(indent + row + "\n");
        }
    }

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

private static String repeatString(int stack) {
     StringBuilder indent = new StringBuilder();
     for (int i = 0; i < stack; i++) {
        indent.append(" ");
     }
     return indent.toString();
} 
codeskraps
la source
où est repeatString (stack ++); méthode..?
user1912935
2
chaîne statique statique repeatString (int stack) {StringBuilder indent = new StringBuilder (); for (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
codeskraps
L'indentation ne fonctionne pas correctement aux balises de fin. Vous devez changer de } else if (row.startsWith("</")) {partie pour cela: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

Juste pour référence future, voici une solution qui a fonctionné pour moi (grâce à un commentaire que @George Hawkins a posté dans l'une des réponses):

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

Si vous êtes sûr d'avoir un XML valide, celui-ci est simple et évite les arborescences XML DOM. Peut-être a quelques bugs, commentez si vous voyez quelque chose

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

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

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

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

            return pretty.toString().trim();
    }
milosmns
la source
2
où est la méthode repeatString ..?
user1912935
3
chaîne statique statique repeatString (int stack) {StringBuilder indent = new StringBuilder (); for (int i = 0; i <stack; i ++) {indent.append (""); } return indent.toString (); }
codeskraps
Oui [user1912935], ce que @codeskraps a écrit, devrait être assez simple :)
milosmns
Concaténation avec un StringBuilder dans une boucle: mauvaise pratique.
james.garriss
@ james.garriss Mais il est super facile de se diviser en de nouvelles lignes, cela illustre simplement une approche simple sans aucun arbre DOM.
milosmns
5

Toutes les solutions ci-dessus ne fonctionnaient pas pour moi, alors j'ai trouvé ce http://myshittycode.com/2014/02/10/java-properly-indenting-xml-string/

L'indice est de supprimer les espaces blancs avec 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
la source
1
Notez que l'utilisation de la propriété '{ xml.apache.org/xslt } indent -amount' vous liera à une implémentation de transformateur spécifique.
vallismortis
1
De toutes les solutions, celle-ci a fonctionné le mieux. J'avais déjà des espaces et de nouvelles lignes dans mon XML et je ne voulais pas ajouter plus de dépendances à mon projet. J'aimerais ne pas avoir à analyser le XML mais bon.
Fabio
5

Ce code ci-dessous fonctionne parfaitement

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
la source
5

Je les mélange tous et j'écris un petit programme. Il lit le fichier xml et l'imprime. Juste au lieu de xzy, donnez votre chemin de fichier.

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

}

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

    return strWriter.getBuffer().toString();

}
user3083990
la source
4

Juste une autre solution qui fonctionne pour nous

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
la source
3

Utilisation de 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
la source
3

Comme alternative aux réponses de max , codeskraps , David Easley et milosmns , jetez un œil à ma bibliothèque de jolies imprimantes légères et hautes performances: 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
}

Parfois, comme lors de l'exécution de services SOAP simulés directement à partir d'un fichier, il est bon d'avoir une jolie imprimante qui gère également du XML déjà assez imprimé:

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

Comme certains l'ont commenté, la jolie impression n'est qu'un moyen de présenter XML sous une forme plus lisible par l'homme - les espaces n'appartiennent strictement pas à vos données XML.

La bibliothèque est conçue pour une jolie impression à des fins de journalisation, et comprend également des fonctions de filtrage (suppression / anonymisation de sous-arborescence) et de jolie impression de XML dans les nœuds CDATA et Texte.

ThomasRS
la source
2

J'ai eu le même problème et j'ai beaucoup de succès avec JTidy ( http://jtidy.sourceforge.net/index.html )

Exemple:

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
la source
2

Underscore-java a une méthode statique U.formatXml(string). Je suis le responsable du projet. Exemple en direct

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

Production:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <tag>
      <nested>hello</nested>
   </tag>
</root>
Valentyn Kolesnikov
la source
C'est génial!
senyor
1

il y a un très bon utilitaire xml en ligne de commande appelé xmlstarlet ( http://xmlstar.sourceforge.net/ ) qui peut faire beaucoup de choses que beaucoup de gens utilisent.

Vous pouvez exécuter ce programme par programme en utilisant Runtime.exec, puis lire le fichier de sortie formaté. Il a plus d'options et un meilleur rapport d'erreur que quelques lignes de code Java ne peuvent fournir.

téléchargez xmlstarlet: http://sourceforge.net/project/showfiles.php?group_id=66612&package_id=64589

anjanb
la source
1

J'ai trouvé qu'en Java 1.6.0_32, la méthode normale pour imprimer une chaîne XML (en utilisant un Transformer avec un null ou une identité xslt) ne se comporte pas comme je le voudrais si les balises sont simplement séparées par des espaces, par opposition à ne pas avoir de séparation texte. J'ai essayé d'utiliser <xsl:strip-space elements="*"/>mon modèle en vain. La solution la plus simple que j'ai trouvée était de décaper l'espace comme je le souhaitais en utilisant un filtre SAXSource et XML. Étant donné que ma solution était pour la journalisation, j'ai également étendu cela pour travailler avec des fragments XML incomplets. Notez que la méthode normale semble fonctionner correctement si vous utilisez un DOMSource mais je ne voulais pas l'utiliser à cause de l'incomplétude et de la surcharge de mémoire.

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
la source
1

Les solutions que j'ai trouvées ici pour Java 1.6+ ne reformatent pas le code s'il est déjà formaté. Celui qui a fonctionné pour moi (et a reformaté le code déjà formaté) était le suivant.

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

C'est un bon outil à utiliser dans vos tests unitaires pour la comparaison xml de chaîne complète.

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
la source
1

Pour ceux qui recherchent une solution rapide et sale - qui n'a pas besoin que le XML soit valide à 100%. par exemple en cas de journalisation REST / SOAP (on ne sait jamais ce que les autres envoient ;-))

J'ai trouvé et avancé un code coupé que j'ai trouvé en ligne et qui, je pense, manque toujours ici comme approche possible valide:

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

voici la sortie:

<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
la source
1

J'ai vu une réponse utiliser Scala, alors voici une autre Groovy, juste au cas où quelqu'un la trouverait intéressante. L'indentation par défaut est de 2 étapes, le XmlNodePrinterconstructeur peut également recevoir une autre valeur.

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

Utilisation de Java si le pot groovy est dans le chemin de 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());
vsnyc
la source
1

Dans le cas où vous n'avez pas besoin de beaucoup d'indentation mais de quelques sauts de ligne, il pourrait suffire de simplement regex ...

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

Le code est sympa, pas le résultat à cause d'une indentation manquante.


(Pour les solutions avec indentation, voir les autres réponses.)

comonad
la source
1
Hmmmm ... Juste en pensant fort, qui aurait besoin d'une telle solution? Le seul domaine que je peux voir est celui des données que nous obtenons de certains services Web et juste pour tester ces données et leur validité, le développeur ou le testeur pourrait avoir besoin de telles données. Sinon, ce n'est pas une bonne option ....
Sudhakar Chavali
1
@SudhakarChavali je suis développeur. je pourrais en avoir besoin pour les hacks sales println () et log.debug (); c'est-à-dire que, parfois, je ne peux utiliser que des fichiers journaux à partir d'un environnement de serveur restreint (avec une interface d'administration Web au lieu d'un accès shell) au lieu de déboguer le programme pas à pas.
comonad