Quel est le moyen le plus simple d'obtenir du XML indenté avec des sauts de ligne à partir de XmlDocument?

105

Quand je construis du XML à partir de zéro avec XmlDocument, la OuterXmlpropriété a déjà tout bien en retrait avec des sauts de ligne. Cependant, si j'appelle LoadXmlun XML très "compressé" (sans saut de ligne ni indentation), la sortie de OuterXmlreste ainsi. Alors ...

Quel est le moyen le plus simple d'obtenir une sortie XML embellie à partir d'une instance de XmlDocument?

Neil C. Obremski
la source

Réponses:

209

Sur la base des autres réponses, j'ai examiné XmlTextWriteret proposé la méthode d'aide suivante:

static public string Beautify(this XmlDocument doc)
{
    StringBuilder sb = new StringBuilder();
    XmlWriterSettings settings = new XmlWriterSettings
    {
        Indent = true,
        IndentChars = "  ",
        NewLineChars = "\r\n",
        NewLineHandling = NewLineHandling.Replace
    };
    using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
        doc.Save(writer);
    }
    return sb.ToString();
}

C'est un peu plus de code que ce que j'espérais, mais cela fonctionne juste comme de la pêche.

Neil C. Obremski
la source
5
Vous pouvez même envisager de créer votre méthode utilitaire en tant que méthode d'extension de la classe XmlDocument.
Opposition
5
Curieusement, pour moi, cela ne fait rien sauf définir l'encodage de l'en-tête xml sur UTF-16. Curieusement, il le fait même si je définis explicitementsettings.Encoding = Encoding.UTF8;
Nyerguds
3
Le problème d'encodage peut être résolu en utilisant un MemoryStream+ StreamWriteravec un encodage spécifié au lieu de StringBuilder, et en obtenant le texte avec enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. Le résultat final n'est cependant toujours pas formaté. Cela pourrait-il être lié au fait que je commence à partir d'un document lu qui a déjà un formatage? Je veux juste que mes nouveaux nœuds soient également formatés.
Nyerguds
2
Je suis tenté de modifier le "\r\n"to Environment.Newline.
Pharap
2
doc.PreserveWhitespacene doit pas être défini sur true. Sinon, il échoue s'il contient déjà une indentation partielle.
Master DJon
48

Comme adapté du blog d' Erika Ehrli , cela devrait le faire:

XmlDocument doc = new XmlDocument();
doc.LoadXml("<item><name>wrench</name></item>");
// Save the document to a file and auto-indent the output.
using (XmlTextWriter writer = new XmlTextWriter("data.xml", null)) {
    writer.Formatting = Formatting.Indented;
    doc.Save(writer);
}
DocMax
la source
10
la fermeture de l' usinginstruction fermera automatiquement l'écrivain lors de l' Dispose()appel.
Tyler Lee
3
Pour moi, cela n'indique qu'une seule ligne. J'ai encore des dizaines d'autres lignes qui ne sont pas en retrait.
C Johnson
40

Ou encore plus facilement si vous avez accès à Linq

try
{
    RequestPane.Text = System.Xml.Linq.XElement.Parse(RequestPane.Text).ToString();
}
catch (System.Xml.XmlException xex)
{
            displayException("Problem with formating text in Request Pane: ", xex);
}
JFK
la source
très agréable! l' avantage par rapport à la réponse acceptée est qu'il ne produira pas de commentaire XML donc fonctionne mieux pour un fragment XML
Umar Farooq Khawaja
3
Curieusement, cela supprime le <?xml ...?>et le <!DOCTYPE ...>du XML. OK pour un fragment, mais pas souhaitable pour un document complet.
Jesse Chisholm
C'est la seule manière qui a fonctionné pour moi. Toutes les autres méthodes utilisant xmltextwriter, Formatting = Formatting.Indented et XmlWriterSettings ne reformate PAS le texte, mais cette méthode le fait.
kexx
16

Une version de méthode d'extension plus courte

public static string ToIndentedString( this XmlDocument doc )
{
    var stringWriter = new StringWriter(new StringBuilder());
    var xmlTextWriter = new XmlTextWriter(stringWriter) {Formatting = Formatting.Indented};
    doc.Save( xmlTextWriter );
    return stringWriter.ToString();
}
Jonathan Mitchem
la source
Cela fonctionne très bien et n'implique pas la création de fichiers inutiles sur le disque
Zain Rizvi
13

Si la méthode Beautify ci-dessus est appelée pour un XmlDocumentqui contient déjà un XmlProcessingInstructionnœud enfant, l'exception suivante est levée:

Impossible d'écrire la déclaration XML. La méthode WriteStartDocument l'a déjà écrite.

Ceci est ma version modifiée de l'original pour se débarrasser de l'exception:

private static string beautify(
    XmlDocument doc)
{
    var sb = new StringBuilder();
    var settings =
        new XmlWriterSettings
            {
                Indent = true,
                IndentChars = @"    ",
                NewLineChars = Environment.NewLine,
                NewLineHandling = NewLineHandling.Replace,
            };

    using (var writer = XmlWriter.Create(sb, settings))
    {
        if (doc.ChildNodes[0] is XmlProcessingInstruction)
        {
            doc.RemoveChild(doc.ChildNodes[0]);
        }

        doc.Save(writer);
        return sb.ToString();
    }
}

Cela fonctionne pour moi maintenant, vous auriez probablement besoin de scanner tous les nœuds enfants pour le XmlProcessingInstructionnœud, pas seulement le premier?


Mise à jour avril 2015:

Depuis que j'ai eu un autre cas où le codage était incorrect, j'ai cherché comment appliquer UTF-8 sans BOM. J'ai trouvé ce billet de blog et créé une fonction basée sur celui-ci:

private static string beautify(string xml)
{
    var doc = new XmlDocument();
    doc.LoadXml(xml);

    var settings = new XmlWriterSettings
    {
        Indent = true,
        IndentChars = "\t",
        NewLineChars = Environment.NewLine,
        NewLineHandling = NewLineHandling.Replace,
        Encoding = new UTF8Encoding(false)
    };

    using (var ms = new MemoryStream())
    using (var writer = XmlWriter.Create(ms, settings))
    {
        doc.Save(writer);
        var xmlString = Encoding.UTF8.GetString(ms.ToArray());
        return xmlString;
    }
}
Uwe Keim
la source
cela ne fonctionnera pas si vous mettez la section cdata dans le nœud parent et avant le nœud enfant
Sasha Bond
2
MemoryStream ne semble pas nécessaire, du moins de mon côté. Dans les paramètres que j'ai définis: Encoding = Encoding.UTF8etOmitXmlDeclaration = true
Master DJon
7
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;
benPearce
la source
5
    public static string FormatXml(string xml)
    {
        try
        {
            var doc = XDocument.Parse(xml);
            return doc.ToString();
        }
        catch (Exception)
        {
            return xml;
        }
    }
resserré
la source
La réponse ci-dessous pourrait certainement être expliquée, mais cela a fonctionné pour moi et est beaucoup plus simple que les autres solutions.
CarlR
Il semble que vous deviez importer l'assemblage system.link.XML pour que cela fonctionne sur PS 3.
CarlR
2

Un moyen simple consiste à utiliser:

writer.WriteRaw(space_char);

Comme cet exemple de code, ce code est ce que j'ai utilisé pour créer une vue arborescente comme une structure à l'aide de XMLWriter:

private void generateXML(string filename)
        {
            using (XmlWriter writer = XmlWriter.Create(filename))
            {
                writer.WriteStartDocument();
                //new line
                writer.WriteRaw("\n");
                writer.WriteStartElement("treeitems");
                //new line
                writer.WriteRaw("\n");
                foreach (RootItem root in roots)
                {
                    //indent
                    writer.WriteRaw("\t");
                    writer.WriteStartElement("treeitem");
                    writer.WriteAttributeString("name", root.name);
                    writer.WriteAttributeString("uri", root.uri);
                    writer.WriteAttributeString("fontsize", root.fontsize);
                    writer.WriteAttributeString("icon", root.icon);
                    if (root.children.Count != 0)
                    {
                        foreach (ChildItem child in children)
                        {
                            //indent
                            writer.WriteRaw("\t");
                            writer.WriteStartElement("treeitem");
                            writer.WriteAttributeString("name", child.name);
                            writer.WriteAttributeString("uri", child.uri);
                            writer.WriteAttributeString("fontsize", child.fontsize);
                            writer.WriteAttributeString("icon", child.icon);
                            writer.WriteEndElement();
                            //new line
                            writer.WriteRaw("\n");
                        }
                    }
                    writer.WriteEndElement();
                    //new line
                    writer.WriteRaw("\n");
                }

                writer.WriteEndElement();
                writer.WriteEndDocument();

            }

        }

De cette façon, vous pouvez ajouter des sauts de tabulation ou de ligne comme vous en avez l'habitude, c'est-à-dire \ t ou \ n

Munim Dibosh
la source
1

Lors de la mise en œuvre des suggestions publiées ici, j'ai eu des problèmes avec l'encodage du texte. Il semble que le codage du XmlWriterSettingsest ignoré, et toujours remplacé par le codage du flux. Lorsque vous utilisez a StringBuilder, il s'agit toujours du codage de texte utilisé en interne en C #, à savoir UTF-16.

Voici donc une version qui prend également en charge d'autres encodages.

REMARQUE IMPORTANTE: la mise en forme est complètement ignorée si votre XMLDocumentobjet a sa preserveWhitespacepropriété activée lors du chargement du document. Cela m'a laissé perplexe pendant un moment, alors assurez-vous de ne pas l'activer.

Mon code final:

public static void SaveFormattedXml(XmlDocument doc, String outputPath, Encoding encoding)
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    settings.IndentChars = "\t";
    settings.NewLineChars = "\r\n";
    settings.NewLineHandling = NewLineHandling.Replace;

    using (MemoryStream memstream = new MemoryStream())
    using (StreamWriter sr = new StreamWriter(memstream, encoding))
    using (XmlWriter writer = XmlWriter.Create(sr, settings))
    using (FileStream fileWriter = new FileStream(outputPath, FileMode.Create))
    {
        if (doc.ChildNodes.Count > 0 && doc.ChildNodes[0] is XmlProcessingInstruction)
            doc.RemoveChild(doc.ChildNodes[0]);
        // save xml to XmlWriter made on encoding-specified text writer
        doc.Save(writer);
        // Flush the streams (not sure if this is really needed for pure mem operations)
        writer.Flush();
        // Write the underlying stream of the XmlWriter to file.
        fileWriter.Write(memstream.GetBuffer(), 0, (Int32)memstream.Length);
    }
}

Cela enregistrera le xml formaté sur le disque, avec l'encodage de texte donné.

Nyerguds
la source
1

Si vous avez une chaîne de XML, plutôt qu'un document prêt à l'emploi, vous pouvez le faire de cette façon:

var xmlString = "<xml>...</xml>"; // Your original XML string that needs indenting.
xmlString = this.PrettifyXml(xmlString);

private string PrettifyXml(string xmlString)
{
    var prettyXmlString = new StringBuilder();

    var xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(xmlString);

    var xmlSettings = new XmlWriterSettings()
    {
        Indent = true,
        IndentChars = " ",
        NewLineChars = "\r\n",
        NewLineHandling = NewLineHandling.Replace
    };

    using (XmlWriter writer = XmlWriter.Create(prettyXmlString, xmlSettings))
    {
        xmlDoc.Save(writer);
    }

    return prettyXmlString.ToString();
}
theJerm
la source
1

Une approche plus simplifiée basée sur la réponse acceptée:

static public string Beautify(this XmlDocument doc) {
    StringBuilder sb = new StringBuilder();
    XmlWriterSettings settings = new XmlWriterSettings
    {
        Indent = true
    };

    using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
        doc.Save(writer);
    }

    return sb.ToString(); 
}

La définition de la nouvelle ligne n'est pas nécessaire. Les caractères de retrait ont également les deux espaces par défaut, j'ai donc préféré ne pas les définir également.

dijoe
la source