Formater une chaîne XML pour imprimer une chaîne XML conviviale


187

J'ai une chaîne XML en tant que telle:

<?xml version='1.0'?><response><error code='1'> Success</error></response>

Il n'y a pas de lignes entre un élément et un autre et est donc très difficile à lire. Je veux une fonction qui formate la chaîne ci-dessus:

<?xml version='1.0'?>
<response>
<error code='1'> Success</error>
</response> 

Sans avoir recours à l'écriture manuelle de la fonction de format moi-même, y a-t-il une bibliothèque .Net ou un extrait de code que je peux utiliser à la légère?


1
accessoires au CMS, la question est un double stackoverflow.com/questions/203528
Spence

2
Pas un double. Celui-là précise XmlDocumentce qui disqualifierait la réponse la plus votée sur cette question.
sirdank

Réponses:


191

Utilisez XmlTextWriter...

public static string PrintXML(string xml)
{
    string result = "";

    MemoryStream mStream = new MemoryStream();
    XmlTextWriter writer = new XmlTextWriter(mStream, Encoding.Unicode);
    XmlDocument document = new XmlDocument();

    try
    {
        // Load the XmlDocument with the XML.
        document.LoadXml(xml);

        writer.Formatting = Formatting.Indented;

        // Write the XML into a formatting XmlTextWriter
        document.WriteContentTo(writer);
        writer.Flush();
        mStream.Flush();

        // Have to rewind the MemoryStream in order to read
        // its contents.
        mStream.Position = 0;

        // Read MemoryStream contents into a StreamReader.
        StreamReader sReader = new StreamReader(mStream);

        // Extract the text from the StreamReader.
        string formattedXml = sReader.ReadToEnd();

        result = formattedXml;
    }
    catch (XmlException)
    {
        // Handle the exception
    }

    mStream.Close();
    writer.Close();

    return result;
}

7
Cela fonctionne si vous avez affaire à du code qui se trouve sur une ancienne version du framework .NET pré-LINQ, mais l'autre exemple est beaucoup plus propre.
Mike

8
Pour clarifier le commentaire de Mike: LINQ a été introduit dans .NET 3.5. Donc, si vous utilisez une version de .NET plus ancienne (.NET 1, 1.1, 2 ou 3.0), vous devrez utiliser cette réponse. Mais si vous utilisez .NET 3.5 ou une version ultérieure, la réponse de Charles Prakash Dasari est beaucoup plus simple.
Simon Tewsi

1
@ SM Kamran J'utilise votre code mais j'obtiens une erreur ressemblant à {"Impossible d'accéder à un Stream fermé."} Sur writer.Close (); les pls donnent la solution.
Jatin Gadhiya

@JatinGadhiya J'ai eu le même problème et je l'ai résolu en utilisant {using block} pour définir les flux. de cette manière, vous n'avez pas besoin de fermer le flux manuellement et les flux seront fermés automatiquement lorsque vous atteindrez la fin du bloc d'utilisation.
Vahid Farahmandian

323

Vous devrez analyser le contenu d'une manière ou d'une autre ... Je trouve que l'utilisation de LINQ est le moyen le plus simple de le faire. Encore une fois, tout dépend de votre scénario exact. Voici un exemple de travail utilisant LINQ pour formater une chaîne XML d'entrée.

string FormatXml(string xml)
{
     try
     {
         XDocument doc = XDocument.Parse(xml);
         return doc.ToString();
     }
     catch (Exception)
     {
         // Handle and throw if fatal exception here; don't just ignore them
         return xml;
     }
 }

[les instructions d'utilisation sont omises par souci de concision]


Cela va-t-il affecter strictement les sauts de ligne et l'indentation? Je ne veux pas d'autres changements, comme "0" étant changé en "0.0", etc.
Radim Cernej

3
@radim Oui. Aucune modification des données réelles ne sera effectuée. Seules les balises seront formatées et mises en retrait.
Charles Prakash Dasari

2
J'ai remarqué que cela fonctionnait bien avec UTF8, mais pas avec le contenu des fichiers XML Unicode.
Nayan

1
@SteveWellens, vous pouvez accéder à la déclaration via doc.Declaration.ToString() + doc.ToString()ou en utilisant à la doc.Saveplace de doc.ToString. Voir ce lien pour plus de détails.
David French

1
Suggérez d'inclure les espaces de noms car cela évite aux utilisateurs d'avoir à chercher un espace de noms pour une classe qu'ils n'ont peut-être pas beaucoup utilisée auparavant. using System.Xml.Linq; Fonctionne bien Merci!
Scott Moniz

61

Celui-ci, de kristopherjohnson est beaucoup mieux:

  1. Il ne nécessite pas non plus d'en-tête de document XML.
  2. A des exceptions plus claires
  3. Ajoute des options de comportement supplémentaires: OmitXmlDeclaration = true, NewLineOnAttributes = true
  4. Moins de lignes de code

    static string PrettyXml(string xml)
    {
        var stringBuilder = new StringBuilder();
    
        var element = XElement.Parse(xml);
    
        var settings = new XmlWriterSettings();
        settings.OmitXmlDeclaration = true;
        settings.Indent = true;
        settings.NewLineOnAttributes = true;
    
        using (var xmlWriter = XmlWriter.Create(stringBuilder, settings))
        {
            element.Save(xmlWriter);
        }
    
        return stringBuilder.ToString();
    }
    

Todd, pourriez-vous clarifier ce que vous entendez par «ne nécessite pas d'en-tête de document XML»? J'ai essayé la solution de Charles Prakash Dasari et je viens de passer un fragment XML sans déclaration xml (c'est-à-dire sans la <?xml version="1.0" encoding="UTF-8" ?>ligne en haut) et cela a bien fonctionné.
Simon Tewsi

3
Par rapport à la réponse acceptée. Comparé à Charles, celui-ci aurait une meilleure configurabilité. Cependant, j'utiliserais probablement moi-même la méthode Charlies à l'avenir, une telle configurabilité serait une exigence rare.
Todd

1
Celui-ci est beaucoup mieux et plus court
Alex Jolig

9

La solution simple qui fonctionne pour moi:

        XmlDocument xmlDoc = new XmlDocument();
        StringWriter sw = new StringWriter();
        xmlDoc.LoadXml(rawStringXML);
        xmlDoc.Save(sw);
        String formattedXml = sw.ToString();

1
cela crée un fichier xml avec <? xml version = "1.0" encoding = "utf-16"?> comme en-tête. Cela n'a pas été analysé par XmlSerializer, avec l'erreur «Il n'y a pas de marque d'ordre d'octet Unicode». Le correctif consistait à supprimer encoding = "utf-16", voir: stackoverflow.com/questions/29915467/… .
Declan Taylor

7

Vérifiez le lien suivant: Comment joliment imprimer du XML (Malheureusement, le lien renvoie maintenant 404 :()

La méthode dans le lien prend une chaîne XML comme argument et renvoie une chaîne XML bien formée (en retrait).

Je viens de copier l'exemple de code à partir du lien pour rendre cette réponse plus complète et plus pratique.

public static String PrettyPrint(String XML)
{
    String Result = "";

    MemoryStream MS = new MemoryStream();
    XmlTextWriter W = new XmlTextWriter(MS, Encoding.Unicode);
    XmlDocument D   = new XmlDocument();

    try
    {
        // Load the XmlDocument with the XML.
        D.LoadXml(XML);

        W.Formatting = Formatting.Indented;

        // Write the XML into a formatting XmlTextWriter
        D.WriteContentTo(W);
        W.Flush();
        MS.Flush();

        // Have to rewind the MemoryStream in order to read
        // its contents.
        MS.Position = 0;

        // Read MemoryStream contents into a StreamReader.
        StreamReader SR = new StreamReader(MS);

        // Extract the text from the StreamReader.
        String FormattedXML = SR.ReadToEnd();

        Result = FormattedXML;
    }
    catch (XmlException)
    {
    }

    MS.Close();
    W.Close();

    return Result;
}

2
Fonctionne très bien pour moi, je viens d'en faire une méthode d'extension de chaîne. De plus, ce site Web est en panne, donc c'est bien que vous ayez obtenu une copie ...
goodguys_activate

1
Réponse en double. @SM Kamran publie également la même réponse.
Vahid Farahmandian

@VahidFarahmandian Oui. Je ne pouvais pas faire grand-chose à ce sujet parce que j'avais posté 1 minute plus tôt que lui :) BTW, j'essayais d'ajouter d'où venait la réponse pour donner le crédit à l'affiche du blog. Malheureusement, le lien est rompu maintenant :(.
Chansik Im

J'aime bien cette réponse par rapport à celle de Charles (FormatXml) et Todd (PrettyXml), car cette réponse ne supprime pas la <?xml...?>ligne. Cette réponse correspond à ce que j'avais à l'esprit au départ. Le seul point négatif serait que je préférerais les tabulations plutôt que les espaces utilisés nativement. Je me suis fixé Indentation = 1et IndentChar = '\t'pour obtenir exactement ce que je voulais.
Sarah Weinberger

@ CHICoder007 Merci pour le commentaire sur la méthode d'extension. Tu m'as appris quelque chose de nouveau. L'ajout d'un (this String XML)fonctionne très bien.
Sarah Weinberger

4

J'ai essayé:

internal static void IndentedNewWSDLString(string filePath)
{
    var xml = File.ReadAllText(filePath);
    XDocument doc = XDocument.Parse(xml);
    File.WriteAllText(filePath, doc.ToString());
}

cela fonctionne bien comme prévu.


mais cela supprime la balise <? xml?> en haut
Juran

2

.NET 2.0 ignorant la résolution de noms, et avec une élimination des ressources, une indentation, une conservation des espaces blancs et un encodage personnalisé appropriés :

public static string Beautify(System.Xml.XmlDocument doc)
{
    string strRetValue = null;
    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    // enc = new System.Text.UTF8Encoding(false);

    System.Xml.XmlWriterSettings xmlWriterSettings = new System.Xml.XmlWriterSettings();
    xmlWriterSettings.Encoding = enc;
    xmlWriterSettings.Indent = true;
    xmlWriterSettings.IndentChars = "    ";
    xmlWriterSettings.NewLineChars = "\r\n";
    xmlWriterSettings.NewLineHandling = System.Xml.NewLineHandling.Replace;
    //xmlWriterSettings.OmitXmlDeclaration = true;
    xmlWriterSettings.ConformanceLevel = System.Xml.ConformanceLevel.Document;


    using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
    {
        using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(ms, xmlWriterSettings))
        {
            doc.Save(writer);
            writer.Flush();
            ms.Flush();

            writer.Close();
        } // End Using writer

        ms.Position = 0;
        using (System.IO.StreamReader sr = new System.IO.StreamReader(ms, enc))
        {
            // Extract the text from the StreamReader.
            strRetValue = sr.ReadToEnd();

            sr.Close();
        } // End Using sr

        ms.Close();
    } // End Using ms


    /*
    System.Text.StringBuilder sb = new System.Text.StringBuilder(); // Always yields UTF-16, no matter the set encoding
    using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(sb, settings))
    {
        doc.Save(writer);
        writer.Close();
    } // End Using writer
    strRetValue = sb.ToString();
    sb.Length = 0;
    sb = null;
    */

    xmlWriterSettings = null;
    return strRetValue;
} // End Function Beautify

Usage:

System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.XmlResolver = null;
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("C:\Test.svg");
string SVG = Beautify(xmlDoc);

1

Sortie Pretty XML personnalisable avec déclaration XML UTF-8

La définition de classe suivante donne une méthode simple pour convertir une chaîne XML d'entrée en XML de sortie formaté avec la déclaration xml en UTF-8. Il prend en charge toutes les options de configuration offertes par la classe XmlWriterSettings .

using System;
using System.Text;
using System.Xml;
using System.IO;

namespace CJBS.Demo
{
    /// <summary>
    /// Supports formatting for XML in a format that is easily human-readable.
    /// </summary>
    public static class PrettyXmlFormatter
    {

        /// <summary>
        /// Generates formatted UTF-8 XML for the content in the <paramref name="doc"/>
        /// </summary>
        /// <param name="doc">XmlDocument for which content will be returned as a formatted string</param>
        /// <returns>Formatted (indented) XML string</returns>
        public static string GetPrettyXml(XmlDocument doc)
        {
            // Configure how XML is to be formatted
            XmlWriterSettings settings = new XmlWriterSettings 
            {
                Indent = true
                , IndentChars = "  "
                , NewLineChars = System.Environment.NewLine
                , NewLineHandling = NewLineHandling.Replace
                //,NewLineOnAttributes = true
                //,OmitXmlDeclaration = false
            };

            // Use wrapper class that supports UTF-8 encoding
            StringWriterWithEncoding sw = new StringWriterWithEncoding(Encoding.UTF8);

            // Output formatted XML to StringWriter
            using (XmlWriter writer = XmlWriter.Create(sw, settings))
            {
                doc.Save(writer);
            }

            // Get formatted text from writer
            return sw.ToString();
        }



        /// <summary>
        /// Wrapper class around <see cref="StringWriter"/> that supports encoding.
        /// Attribution: http://stackoverflow.com/a/427737/3063884
        /// </summary>
        private sealed class StringWriterWithEncoding : StringWriter
        {
            private readonly Encoding encoding;

            /// <summary>
            /// Creates a new <see cref="PrettyXmlFormatter"/> with the specified encoding
            /// </summary>
            /// <param name="encoding"></param>
            public StringWriterWithEncoding(Encoding encoding)
            {
                this.encoding = encoding;
            }

            /// <summary>
            /// Encoding to use when dealing with text
            /// </summary>
            public override Encoding Encoding
            {
                get { return encoding; }
            }
        }
    }
}

Possibilités d'amélioration supplémentaire: -

  • Une méthode supplémentaire GetPrettyXml(XmlDocument doc, XmlWriterSettings settings)pourrait être créée pour permettre à l'appelant de personnaliser la sortie.
  • Une méthode supplémentaire GetPrettyXml(String rawXml)pourrait être ajoutée qui prend en charge l'analyse du texte brut, plutôt que de laisser le client utiliser le XmlDocument. Dans mon cas, j'avais besoin de manipuler le XML en utilisant le XmlDocument, donc je n'ai pas ajouté ceci.

Usage:

String myFormattedXml = null;
XmlDocument doc = new XmlDocument();
try
{
    doc.LoadXml(myRawXmlString);
    myFormattedXml = PrettyXmlFormatter.GetPrettyXml(doc);
}
catch(XmlException ex)
{
    // Failed to parse XML -- use original XML as formatted XML
    myFormattedXml = myRawXmlString;
}

0

si vous chargez le XMLDoc, je suis presque sûr que la fonction .ToString () possède une surcharge pour cela.

Mais est-ce pour le débogage? La raison pour laquelle il est envoyé comme ça est de prendre moins d'espace (c'est-à-dire en supprimant les espaces inutiles du XML).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.