Was ist der einfachste Weg, um eingerücktes XML mit Zeilenumbrüchen von XmlDocument zu erhalten?

105

Wenn ich XML von Grund auf neu aufbaue XmlDocument, hat die OuterXmlEigenschaft bereits alles schön eingerückt mit Zeilenumbrüchen. Wenn ich jedoch LoadXmlsehr "komprimiertes" XML aufrufe (keine Zeilenumbrüche oder Einrückungen), OuterXmlbleibt die Ausgabe von so. So ...

Was ist der einfachste Weg, um eine verschönerte XML-Ausgabe von einer Instanz von zu erhalten XmlDocument?

Neil C. Obremski
quelle

Antworten:

209

Basierend auf den anderen Antworten habe ich XmlTextWriterdie folgende Hilfsmethode untersucht und gefunden:

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

Es ist ein bisschen mehr Code als ich gehofft hatte, aber es funktioniert nur pfirsichfarben.

Neil C. Obremski
quelle
5
Sie können sogar in Betracht ziehen, Ihre Dienstprogrammmethode als Erweiterungsmethode für die XmlDocument-Klasse zu erstellen.
Oppositionelle
5
Seltsamerweise bedeutet dies für mich nichts anderes, als die Codierung des XML-Headers auf UTF-16 zu setzen. Seltsamerweise tut es dies auch, wenn ich es explizit einstellesettings.Encoding = Encoding.UTF8;
Nyerguds
3
Das Codierungsproblem kann gelöst werden, indem ein MemoryStream+ StreamWritermit einer angegebenen Codierung anstelle von verwendet StringBuilderwird und der Text mit abgerufen wird enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. Das Endergebnis ist jedoch noch in keiner Weise formatiert. Könnte es sein, dass ich von einem gelesenen Dokument ausgehe, das bereits formatiert ist? Ich möchte nur, dass meine neuen Knoten auch formatiert werden.
Nyerguds
2
Ich bin versucht , das zu ändern , "\r\n"zu Environment.Newline.
Pharap
2
doc.PreserveWhitespacesollte nicht auf true gesetzt werden. Andernfalls schlägt es fehl, wenn es bereits teilweise Einrückungen enthält.
Meister DJon
48

Wie aus Erika Ehrlis Blog hervorgeht, sollte dies Folgendes tun:

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
quelle
10
Durch das Schließen der usingAnweisung wird der Writer automatisch geschlossen, wenn er Dispose()aufgerufen wird.
Tyler Lee
3
Für mich wird nur eine Zeile eingerückt. Ich habe noch Dutzende anderer Zeilen, die nicht eingerückt sind.
C Johnson
40

Oder noch einfacher, wenn Sie Zugriff auf Linq haben

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
quelle
Sehr schön! Der Vorteil gegenüber der akzeptierten Antwort ist, dass kein XML-Kommentar erstellt wird, sodass dies für ein XML-Fragment besser funktioniert
Umar Farooq Khawaja,
3
Seltsamerweise wird dadurch das <?xml ...?>und das <!DOCTYPE ...>aus dem XML entfernt. OK für ein Fragment, aber nicht wünschenswert für ein vollständiges Dokument.
Jesse Chisholm
Dies ist der einzige Weg, der für mich funktioniert hat. Alle anderen Methoden, die xmltextwriter verwenden, Formatting = Formatting.Indented und XmlWriterSettings, formatieren den Text NICHT neu, diese Methode jedoch.
Kexx
16

Eine kürzere Version der Erweiterungsmethode

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
quelle
Dies funktioniert sehr gut und beinhaltet nicht das Erstellen unnötiger Dateien auf der Festplatte
Zain Rizvi
13

Wenn die obige Beautify-Methode für einen Knoten aufgerufen wird XmlDocument, der bereits einen untergeordneten XmlProcessingInstructionKnoten enthält , wird die folgende Ausnahme ausgelöst:

XML-Deklaration kann nicht geschrieben werden. Die WriteStartDocument-Methode hat es bereits geschrieben.

Dies ist meine modifizierte Version des Originals, um die Ausnahme zu beseitigen:

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

Es funktioniert jetzt für mich, wahrscheinlich müssten Sie alle untergeordneten Knoten nach dem XmlProcessingInstructionKnoten durchsuchen, nicht nur den ersten?


Update April 2015:

Da ich einen anderen Fall hatte, in dem die Codierung falsch war, suchte ich nach Möglichkeiten, UTF-8 ohne Stückliste zu erzwingen. Ich habe diesen Blog-Beitrag gefunden und eine darauf basierende Funktion erstellt:

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
quelle
es wird nicht funktionieren, wenn Sie cdata Abschnitt innerhalb des übergeordneten Knotens und vor dem untergeordneten Knoten
Sasha Bond
2
MemoryStream scheint zumindest auf meiner Seite nicht benötigt zu werden. In Einstellungen, die ich eingestellt habe: Encoding = Encoding.UTF8undOmitXmlDeclaration = true
Master DJon
7
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;
benPearce
quelle
5
    public static string FormatXml(string xml)
    {
        try
        {
            var doc = XDocument.Parse(xml);
            return doc.ToString();
        }
        catch (Exception)
        {
            return xml;
        }
    }
neu verkabelt
quelle
Die Antwort unten könnte definitiv eine Erklärung gebrauchen, aber sie hat bei mir funktioniert und ist viel einfacher als die anderen Lösungen.
CarlR
Es scheint, dass Sie die system.link.XML-Assembly importieren müssen, damit dies auf PS 3
funktioniert
2

Ein einfacher Weg ist zu verwenden:

writer.WriteRaw(space_char);

Wie dieser Beispielcode habe ich diesen Code verwendet, um mit XMLWriter eine Struktur wie eine Baumansicht zu erstellen:

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

            }

        }

Auf diese Weise können Sie Tabulatoren oder Zeilenumbrüche hinzufügen, wie Sie es normalerweise gewohnt sind, dh \ t oder \ n

Munim Dibosh
quelle
1

Bei der Umsetzung der hier veröffentlichten Vorschläge hatte ich Probleme mit der Textcodierung. Es scheint, dass die Codierung von XmlWriterSettingsignoriert und immer durch die Codierung des Streams überschrieben wird. Bei Verwendung einesStringBuilder ist dies immer die intern in C # verwendete Textcodierung, nämlich UTF-16.

Hier ist also eine Version, die auch andere Codierungen unterstützt.

WICHTIGER HINWEIS: Die Formatierung wird vollständig ignoriert, wenn die Eigenschaft Ihres XMLDocumentObjekts preserveWhitespacebeim Laden des Dokuments aktiviert ist. Das hat mich eine Weile verblüfft, also stellen Sie sicher, dass Sie das nicht aktivieren.

Mein letzter Code:

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

Dadurch wird die formatierte XML mit der angegebenen Textcodierung auf der Festplatte gespeichert.

Nyerguds
quelle
1

Wenn Sie eine XML-Zeichenfolge anstelle eines gebrauchsfertigen Dokuments haben, können Sie dies folgendermaßen tun:

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
quelle
1

Ein vereinfachter Ansatz basierend auf der akzeptierten Antwort:

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

Das Einstellen der neuen Zeile ist nicht erforderlich. Einrückungszeichen haben auch die Standard-Leerzeichen, daher habe ich es vorgezogen, sie nicht ebenfalls festzulegen.

Dijoe
quelle