Який найпростіший спосіб отримати відступ XML з розривами рядків від XmlDocument?


105

Коли я будую XML з нуля XmlDocument, у OuterXmlвластивості вже є все, що добре розрізане з розривами рядків. Однак, якщо я запускаю LoadXmlдуже «стислий» XML (без розривів рядків і відступу), то вихід OuterXmlзалишається таким. Так ...

Який найпростіший спосіб отримати прикрашений вихід XML з екземпляра XmlDocument?

Відповіді:


209

Виходячи з інших відповідей, я вивчив XmlTextWriterта придумав наступний помічний метод:

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

Це трохи більше коду, ніж я сподівався, але він працює просто персиково.


5
Ви навіть можете розглянути можливість створення утиліти як методу розширення до класу XmlDocument.
Опозиційний

5
Як не дивно, для мене це не робить нічого, крім встановлення кодування заголовка xml на UTF-16. Як не дивно, це робиться, навіть якщо я прямо встановивsettings.Encoding = Encoding.UTF8;
Nyerguds

3
Проблему кодування можна вирішити, використовуючи MemoryStream+ StreamWriterіз вказаним кодуванням замість StringBuilder, а також отримуючи текст за допомогою enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. Кінцевий результат все ще жодним чином не форматується. Чи може бути пов'язано, що я починаю з прочитаного документа, який вже має форматування? Я просто хочу, щоб і мої нові вузли були відформатовані.
Nyerguds

2
Мене спокуса змінити "\r\n"на Environment.Newline.
Фарап

2
doc.PreserveWhitespaceне слід встановлювати на істину. В іншому випадку він не вдається, якщо він містить вже часткове відступ.
Майстер DJon

48

Як адаптували з блогу Еріки Ерлі , це слід зробити:

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
закриття usingзаяви автоматично закриє письменника при Dispose()виклику.
Тайлер Лі

3
Для мене це лише відступ одного рядка. У мене ще є десятки інших рядків, які не мають відступу.
C Джонсон

40

Або ще простіше, якщо у вас є доступ до 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);
}

дуже хороша! непогано перевага над загальноприйнятому відповіддю є те , що він не буде створювати XML - коментар так краще працює для фрагмента XML
Умар Фарук Khawaja

3
Як не дивно, це видаляє <?xml ...?>і <!DOCTYPE ...>з XML. Гаразд для фрагмента, але не бажано для повного документа.
Джессі Чісгольм

Це єдиний спосіб, який працював на мене. Усі інші методи, що використовують xmltextwriter, Formatting = Formatting.Indented і XmlWriterSettings, НЕ переформатують текст, але цей метод робить.
kexx

16

Більш коротка версія способу розширення

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

Це працює дуже добре і не передбачає створення непотрібних файлів на диску
Zain Rizvi

13

Якщо вищевказаний метод Beautify викликається для того, XmlDocumentщо вже містить XmlProcessingInstructionдочірній вузол, викидається наступний виняток:

Неможливо записати XML-декларацію. Метод WriteStartDocument вже написав це.

Це моя змінена версія оригіналу для позбавлення від винятку:

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

Він працює для мене зараз, напевно, вам потрібно було б сканувати всі дочірні вузли на XmlProcessingInstructionвузол, а не лише перший?


Оновлення квітня 2015 року:

Оскільки у мене був інший випадок, коли кодування було неправильним, я шукав, як застосувати UTF-8 без BOM. Я знайшов цю публікацію в блозі та створив на її основі функцію:

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

це не спрацює, якщо ви помістите розділ cdata всередині батьківського вузла та перед дочірнім вузлом
Саша Бонд

2
Схоже, MemoryStream не потрібен, принаймні з мого боку. У налаштуваннях я встановив: Encoding = Encoding.UTF8іOmitXmlDeclaration = true
Master DJon


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

Відповідь нижче, безумовно, може стосуватися деяких пояснень, проте це працювало для мене і набагато простіше, ніж інші рішення.
КарлР

Здається, вам потрібно імпортувати збірку system.link.XML, щоб це працювало на PS 3.
CarlR

2

Простий спосіб - це використовувати:

writer.WriteRaw(space_char);

Як і цей зразок коду, цей код - це те, що я використовував для створення структури дерев яного вигляду за допомогою 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();

            }

        }

Таким чином, ви можете додати розриви вкладок чи рядків у звичному звичному режимі, тобто \ t або \ n


1

Реалізуючи запропоновані тут пропозиції, у мене виникли проблеми з кодуванням тексту. Здається, кодування XmlWriterSettingsігнорується і завжди перекривається кодуванням потоку. При використанні a StringBuilderце завжди кодування тексту, яке використовується внутрішньо в C #, а саме UTF-16.

Ось ось версія, яка підтримує і інші кодування.

ВАЖЛИВА ПРИМІТКА. Форматування повністю ігнорується, якщо для вашого XMLDocumentоб’єкта preserveWhitespaceввімкнено властивість під час завантаження документа. Це на мене натрапило на деякий час, тож переконайтеся, що не ввімкніть цього.

Мій кінцевий код:

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

Це дозволить зберегти відформатований xml на диску із заданим кодуванням тексту.


1

Якщо у вас є рядок XML, а не документ, готовий до використання, ви можете це зробити так:

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

Більш спрощений підхід, заснований на прийнятій відповіді:

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

Встановити нову лінію не потрібно. Символи відступів також мають два пробіли за замовчуванням, тому я вважаю за краще не встановлювати його.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.