Qual è il modo più semplice per ottenere XML rientrato con interruzioni di riga da XmlDocument?


105

Quando creo XML da zero con XmlDocument, la OuterXmlproprietà ha già tutto ben rientrato con interruzioni di riga. Tuttavia, se richiamo LoadXmlun XML molto "compresso" (senza interruzioni di riga o rientro), l'output di OuterXmlrimane tale. Così ...

Qual è il modo più semplice per ottenere un output XML abbellito da un'istanza di XmlDocument?

Risposte:


209

Sulla base delle altre risposte, ho esaminato XmlTextWritere ho trovato il seguente metodo di supporto:

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

È un po 'più di codice di quanto speravo, ma funziona alla perfezione.


5
Potresti anche considerare di creare il tuo metodo di utilità come metodo di estensione alla classe XmlDocument.
Oppositivo

5
Stranamente, per me questo non fa altro che impostare la codifica dell'intestazione xml su UTF-16. Stranamente, lo fa anche se ho impostato esplicitamentesettings.Encoding = Encoding.UTF8;
Nyerguds

3
Il problema della codifica può essere risolto usando un MemoryStream+ StreamWritercon una codifica specificata invece di StringBuilder, e ottenendo il testo con enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. Tuttavia, il risultato finale non è ancora formattato. Potrebbe essere correlato al fatto che sto iniziando da un documento letto che ha già una formattazione? Voglio solo che anche i miei nuovi nodi siano formattati.
Nyerguds

2
Sono tentato di modificare il "\r\n"a Environment.Newline.
Pharap

2
doc.PreserveWhitespacenon dovrebbe essere impostato su true. Altrimenti fallisce se contiene già un rientro parziale.
Master DJ il

48

Come adattato dal blog di Erika Ehrli , questo dovrebbe farlo:

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

10
la chiusura usingdell'istruzione chiuderà automaticamente lo scrittore quando Dispose()viene chiamato.
Tyler Lee,

3
Per me, questo rientra solo di una riga. Ho ancora dozzine di altre righe che non sono rientrate.
C Johnson

40

O ancora più facile se hai accesso a 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);
}

molto bella! il vantaggio rispetto alla risposta accettata è che non produrrà un commento XML, quindi funziona meglio per un frammento XML
Umar Farooq Khawaja

3
Stranamente, questo rimuove <?xml ...?>e <!DOCTYPE ...>dall'XML. OK per un frammento, ma non desiderabile per un documento completo.
Jesse Chisholm

Questo è l'unico modo che ha funzionato per me. Tutti gli altri metodi che utilizzano xmltextwriter, Formatting = Formatting.Indented e XmlWriterSettings NON riformattano il testo, ma questo metodo lo fa.
kexx

16

Una versione del metodo di estensione più breve

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

Funziona molto bene e non comporta la creazione di file non necessari su disco
Zain Rizvi

13

Se il metodo Beautify sopra viene chiamato per un nodo XmlDocumentche contiene già un XmlProcessingInstructionnodo figlio, viene generata la seguente eccezione:

Impossibile scrivere la dichiarazione XML. Il metodo WriteStartDocument l'ha già scritto.

Questa è la mia versione modificata di quella originale per eliminare l'eccezione:

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

Per me ora funziona, probabilmente dovresti scansionare tutti i nodi figlio per il XmlProcessingInstructionnodo, non solo il primo?


Aggiornamento aprile 2015:

Dato che ho avuto un altro caso in cui la codifica era sbagliata, ho cercato come applicare UTF-8 senza BOM. Ho trovato questo post del blog e ho creato una funzione basata su di esso:

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

non funzionerà se metti la sezione cdata all'interno del nodo genitore e prima del nodo figlio
Sasha Bond

2
MemoryStream non sembra essere necessario, almeno dalla mia parte. Nelle impostazioni ho impostato: Encoding = Encoding.UTF8eOmitXmlDeclaration = true
Master DJ il

7
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;

5
    public static string FormatXml(string xml)
    {
        try
        {
            var doc = XDocument.Parse(xml);
            return doc.ToString();
        }
        catch (Exception)
        {
            return xml;
        }
    }

La risposta di seguito potrebbe sicuramente avere qualche spiegazione, tuttavia ha funzionato per me ed è molto più semplice delle altre soluzioni.
CarlR

Sembra che sia necessario importare l'assembly system.link.XML affinché funzioni su PS 3.
CarlR

2

Un modo semplice è usare:

writer.WriteRaw(space_char);

Come questo codice di esempio, questo codice è ciò che ho usato per creare una struttura simile a una vista ad albero usando 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();

            }

        }

In questo modo puoi aggiungere tabulazioni o interruzioni di riga nel modo in cui sei normalmente abituato, ad esempio \ t o \ n


1

Durante l'implementazione dei suggerimenti pubblicati qui, ho avuto problemi con la codifica del testo. Sembra che la codifica del XmlWriterSettingsè ignorata e sempre sovrascritta dalla codifica del flusso. Quando si utilizza a StringBuilder, questa è sempre la codifica del testo utilizzata internamente in C #, ovvero UTF-16.

Quindi ecco una versione che supporta anche altre codifiche.

NOTA IMPORTANTE: la formattazione viene completamente ignorata se l' XMLDocumentoggetto ha la sua preserveWhitespaceproprietà abilitata durante il caricamento del documento. Questo mi ha lasciato perplesso per un po ', quindi assicurati di non abilitarlo.

Il mio codice finale:

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

Questo salverà l'xml formattato su disco, con la codifica di testo data.


1

Se hai una stringa di XML, piuttosto che un documento pronto per l'uso, puoi farlo in questo modo:

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

1

Un approccio più semplificato basato sulla risposta accettata:

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

L'impostazione della nuova linea non è necessaria. Anche i caratteri di rientro hanno i due spazi predefiniti, quindi ho preferito non impostarli.

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.