Cách đơn giản nhất để lấy XML được thụt lề có ngắt dòng từ XmlDocument là gì?


105

Khi tôi xây dựng XML từ đầu XmlDocument, thuộc OuterXmltính đã có mọi thứ được thụt lề độc đáo với các dấu ngắt dòng. Tuy nhiên, nếu tôi gọi LoadXmlmột số XML rất "nén" (không có ngắt dòng hoặc thụt lề) thì đầu ra của OuterXmlvẫn như vậy. Vì thế ...

Cách đơn giản nhất để nhận đầu ra XML đẹp từ một phiên bản của là XmlDocumentgì?

Câu trả lời:


209

Dựa trên các câu trả lời khác, tôi đã xem xét XmlTextWritervà đưa ra phương pháp trợ giúp sau:

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

Nó nhiều mã hơn một chút so với tôi mong đợi, nhưng nó hoạt động bình thường.


5
Bạn thậm chí có thể xem xét việc tạo phương thức tiện ích của mình như một phương thức mở rộng cho lớp XmlDocument.
Đối lập

5
Thật kỳ lạ, đối với tôi, điều này không có tác dụng gì ngoại trừ việc đặt mã hóa của tiêu đề xml thành UTF-16. Thật kỳ lạ, nó thực hiện điều này ngay cả khi tôi đặt rõ ràngsettings.Encoding = Encoding.UTF8;
Nyerguds

3
Vấn đề mã hóa có thể được giải quyết bằng cách sử dụng dấu MemoryStream+ StreamWritervới một mã hóa được chỉ định thay vì StringBuildervà lấy văn bản bằng enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. Tuy nhiên, kết quả cuối cùng vẫn không được định dạng. Có thể liên quan đến việc tôi đang bắt đầu từ một tài liệu đã đọc đã có định dạng không? Tôi chỉ muốn các nút mới của mình cũng được định dạng.
Nyerguds

2
Tôi muốn sửa đổi "\r\n"thành Environment.Newline.
Pharap

2
doc.PreserveWhitespacekhông nên được đặt thành true. Ngược lại, nó không thành công nếu nó đã chứa một phần thụt lề.
Master DJon

48

Như được phỏng theo blog của Erika Ehrli , điều này sẽ làm được:

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
việc đóng usingcâu lệnh sẽ tự động đóng trình viết khi Dispose()được gọi.
Tyler Lee

3
Đối với tôi, điều này chỉ thụt lề một dòng. Tôi vẫn còn hàng chục dòng khác không được thụt vào.
C Johnson

40

Hoặc thậm chí dễ dàng hơn nếu bạn có quyền truy cập vào 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);
}

rất đẹp! thumbs up lợi thế hơn câu trả lời được chấp nhận là nó sẽ không tạo ra một bình luận XML để làm việc tốt hơn cho một đoạn XML
Umar Farooq Khawaja

3
Thật kỳ lạ, điều này sẽ loại bỏ <?xml ...?><!DOCTYPE ...>khỏi XML. OK cho một phân đoạn, nhưng không mong muốn cho một tài liệu đầy đủ.
Jesse Chisholm

Đây là cách duy nhất phù hợp với tôi. Tất cả các phương pháp khác sử dụng xmltextwriter, Formatting = Formatting.Indented và XmlWriterSettings KHÔNG định dạng lại văn bản, nhưng phương pháp này thì có.
kexx

16

Một phiên bản phương thức mở rộng ngắn hơn

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

Đây hoạt động rất tốt và không liên quan đến việc tạo ra các file không cần thiết vào đĩa
Zain Rizvi

13

Nếu phương thức Beautify ở trên đang được gọi cho một phương thức XmlDocumentđã chứa một XmlProcessingInstructionnút con thì ngoại lệ sau sẽ được ném ra:

Không thể viết khai báo XML. Phương thức WriteStartDocument đã viết nó.

Đây là phiên bản đã sửa đổi của tôi so với phiên bản gốc để loại bỏ ngoại lệ:

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

Nó hoạt động với tôi bây giờ, có lẽ bạn sẽ cần phải quét tất cả các nút con cho XmlProcessingInstructionnút, không chỉ nút đầu tiên?


Cập nhật tháng 4 năm 2015:

Vì tôi đã gặp một trường hợp mã hóa sai khác, tôi đã tìm kiếm cách thực thi UTF-8 mà không cần BOM. Tôi đã tìm thấy bài đăng trên blog này và tạo một chức năng dựa trên nó:

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

nó sẽ không làm việc nếu bạn đặt phần cdata bên trong nút cha và trước khi nút con
Sasha Bond

2
MemoryStream dường như không cần thiết, ít nhất là về phía tôi. Trong cài đặt tôi thiết lập: Encoding = Encoding.UTF8OmitXmlDeclaration = true
Thạc sĩ Đón

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

Câu trả lời dưới đây chắc chắn có thể làm được với một số lời giải thích tuy nhiên nó phù hợp với tôi và đơn giản hơn nhiều so với các giải pháp khác.
CarlR

Có vẻ như bạn cần nhập tập hợp system.link.XML để điều này hoạt động trên PS 3.
CarlR

2

Một cách đơn giản là sử dụng:

writer.WriteRaw(space_char);

Giống như mã mẫu này, mã này là những gì tôi đã sử dụng để tạo cấu trúc dạng xem dạng cây bằng cách sử dụng 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();

            }

        }

Bằng cách này, bạn có thể thêm tab hoặc ngắt dòng theo cách bạn thường làm, tức là \ t hoặc \ n


1

Khi thực hiện các đề xuất được đăng ở đây, tôi đã gặp sự cố với mã hóa văn bản. Có vẻ như XmlWriterSettingsmã hóa của luồng bị bỏ qua và luôn bị mã hóa của luồng ghi đè. Khi sử dụng mộtStringBuilder , đây luôn là mã hóa văn bản được sử dụng nội bộ trong C #, cụ thể là UTF-16.

Vì vậy, đây là một phiên bản cũng hỗ trợ các mã hóa khác.

LƯU Ý QUAN TRỌNG: Định dạng hoàn toàn bị bỏ qua nếu XMLDocumentđối tượng của bạn đã preserveWhitespacebật thuộc tính khi tải tài liệu. Điều này đã làm tôi bối rối trong một thời gian, vì vậy hãy đảm bảo không bật điều đó.

Mã cuối cùng của tôi:

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

Thao tác này sẽ lưu xml được định dạng vào đĩa, với mã hóa văn bản đã cho.


1

Nếu bạn có một chuỗi XML, thay vì một tài liệu đã sẵn sàng để sử dụng, bạn có thể thực hiện theo cách này:

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

Một cách tiếp cận đơn giản hơn dựa trên câu trả lời được chấp nhận:

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

Đặt dòng mới là không cần thiết. Ký tự thụt lề cũng có hai khoảng trắng mặc định nên tôi không muốn đặt nó nữa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.