Apa cara termudah untuk mendapatkan XML indentasi dengan jeda baris dari XmlDocument?

105

Ketika saya membangun XML dari awal dengan XmlDocument, OuterXmlproperti sudah memiliki semua yang menjorok dengan baik dengan jeda baris. Namun, jika saya memanggil LoadXmlbeberapa XML yang sangat "terkompresi" (tidak ada jeda baris atau indensi) maka keluarannya OuterXmltetap seperti itu. Jadi ...

Apa cara termudah untuk mendapatkan keluaran XML yang dipercantik dari sebuah instance XmlDocument?

Neil C. Obremski
sumber

Jawaban:

209

Berdasarkan jawaban lain, saya mencari XmlTextWriterdan menemukan metode penolong berikut:

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

Ini sedikit lebih banyak kode daripada yang saya harapkan, tetapi berfungsi dengan sangat baik.

Neil C. Obremski
sumber
5
Anda bahkan dapat mempertimbangkan untuk membuat metode utilitas Anda sebagai metode ekstensi ke kelas XmlDocument.
Oposisi
5
Anehnya, bagi saya ini tidak melakukan apa-apa kecuali menyetel pengkodean header xml ke UTF-16. Anehnya, ia melakukan ini bahkan jika saya secara eksplisit mengatursettings.Encoding = Encoding.UTF8;
Nyerguds
3
Masalah pengkodean dapat diselesaikan dengan menggunakan MemoryStream+ StreamWriterdengan pengkodean yang ditentukan, bukan StringBuilder, dan mendapatkan teks dengan enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. Hasil akhirnya masih belum diformat. Mungkinkah terkait bahwa saya memulai dari dokumen baca yang sudah memiliki format? Saya hanya ingin node baru saya diformat juga.
Nyerguds
2
Saya tergoda untuk mengubah "\r\n"menjadi Environment.Newline.
Pharap
2
doc.PreserveWhitespacetidak boleh disetel ke true. Jika tidak, gagal jika sudah berisi indentasi parsial.
Master DJ
48

Seperti yang diadaptasi dari blog Erika Ehrli , berikut ini adalah:

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
sumber
10
penutupan usingpernyataan secara otomatis akan menutup penulis saat Dispose()dipanggil.
Tyler Lee
3
Bagi saya, ini hanya indentasi satu baris. Saya masih memiliki lusinan baris lain yang tidak menjorok ke dalam.
C Johnson
40

Atau bahkan lebih mudah jika Anda memiliki akses ke 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
sumber
sangat bagus! keuntungan jempol atas jawaban yang diterima adalah bahwa itu tidak akan menghasilkan komentar XML sehingga berfungsi lebih baik untuk fragmen XML
Umar Farooq Khawaja
3
Anehnya, ini menghapus <?xml ...?>dan <!DOCTYPE ...>dari XML. OK untuk sebuah fragmen, tetapi tidak diinginkan untuk dokumen lengkap.
Jesse Chisholm
Ini adalah satu-satunya cara yang berhasil bagi saya. Semua metode lain yang menggunakan xmltextwriter, Formatting = Formatting.Indented, dan XmlWriterSettings TIDAK memformat ulang teks, tetapi metode ini melakukannya.
kexx
16

Versi metode ekstensi yang lebih pendek

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
sumber
Ini bekerja dengan sangat baik dan tidak melibatkan pembuatan file yang tidak perlu ke disk
Zain Rizvi
13

Jika metode Beautify di atas dipanggil untuk XmlDocumentyang sudah berisi fileXmlProcessingInstruction node anak, pengecualian berikut akan ditampilkan:

Tidak dapat menulis deklarasi XML. Metode WriteStartDocument telah menulisnya.

Ini adalah versi modifikasi saya dari yang asli untuk menghilangkan pengecualian:

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

Ini berfungsi untuk saya sekarang, mungkin Anda perlu memindai semua node anak untuk XmlProcessingInstructionnode tersebut, bukan hanya yang pertama?


Pembaruan April 2015:

Karena saya memiliki kasus lain di mana pengkodeannya salah, saya mencari cara untuk menerapkan UTF-8 tanpa BOM. Saya menemukan postingan blog ini dan membuat fungsi berdasarkan:

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
sumber
itu tidak akan berfungsi jika Anda meletakkan bagian cdata di dalam node induk dan sebelum node anak
Sasha Bond
2
MemoryStream sepertinya tidak dibutuhkan, setidaknya di pihak saya. Dalam pengaturan saya mengatur: Encoding = Encoding.UTF8danOmitXmlDeclaration = true
Master DJon
7
XmlTextWriter xw = new XmlTextWriter(writer);
xw.Formatting = Formatting.Indented;
benPearce
sumber
5
    public static string FormatXml(string xml)
    {
        try
        {
            var doc = XDocument.Parse(xml);
            return doc.ToString();
        }
        catch (Exception)
        {
            return xml;
        }
    }
rewrew
sumber
Jawaban di bawah ini pasti bisa dilakukan dengan beberapa penjelasan namun berhasil untuk saya dan jauh lebih sederhana daripada solusi lainnya.
CarlR
Sepertinya Anda perlu mengimpor perakitan system.link.XML agar ini berfungsi pada PS 3.
CarlR
2

Cara sederhana adalah dengan menggunakan:

writer.WriteRaw(space_char);

Seperti kode contoh ini, kode ini yang saya gunakan untuk membuat tampilan pohon seperti struktur menggunakan 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();

            }

        }

Dengan cara ini Anda dapat menambahkan tab atau jeda baris dengan cara yang biasa Anda lakukan, yaitu \ t atau \ n

Munim Dibosh
sumber
1

Saat menerapkan saran yang diposting di sini, saya mengalami masalah dengan pengkodean teks. Tampaknya pengkodean XmlWriterSettingsdiabaikan, dan selalu diganti dengan pengkodean aliran. Saat menggunakan a StringBuilder, ini selalu merupakan pengkodean teks yang digunakan secara internal di C #, yaitu UTF-16.

Jadi, inilah versi yang mendukung pengkodean lain juga.

CATATAN PENTING: Pemformatan sepenuhnya diabaikan jika XMLDocumentobjek Anda memilikinyapreserveWhitespace mengaktifkan propertinya saat memuat dokumen. Ini membuat saya bingung untuk sementara waktu, jadi pastikan untuk tidak mengaktifkannya.

Kode terakhir saya:

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

Ini akan menyimpan format xml ke disk, dengan pengkodean teks yang diberikan.

Nyerguds
sumber
1

Jika Anda memiliki string XML, bukan dokumen yang siap digunakan, Anda dapat melakukannya dengan cara ini:

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

Pendekatan yang lebih disederhanakan berdasarkan jawaban yang diterima:

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

Pengaturan baris baru tidak perlu. Karakter indentasi juga memiliki dua spasi default jadi saya lebih suka untuk tidak mengaturnya juga.

dijoe
sumber