Каков самый простой способ получить XML-код с отступом и разрывами строк из XmlDocument?

106

Когда я создаю 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;
Ньергудс
3
Проблема с кодировкой может быть решена путем использования MemoryStream+ StreamWriterс указанной кодировкой вместо StringBuilder, и получения текста с помощью enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. Однако конечный результат по-прежнему никак не отформатирован. Может быть, я начинаю с прочитанного документа, в котором уже есть форматирование? Я просто хочу, чтобы мои новые узлы тоже были отформатированы.
Nyerguds
2
Я испытываю желание изменить "\r\n"To Environment.Newline.
Pharap
2
doc.PreserveWhitespaceне должно быть установлено значение true. В противном случае это не удастся, если он уже содержит частичный отступ.
Master 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);
}
DocMax
источник
10
закрытие usingоператора автоматически закрывает писатель при Dispose()вызове.
Тайлер Ли
3
Для меня это только одна строка. У меня еще есть десятки других строк без отступа.
C Johnson
41

Или даже проще, если у вас есть доступ к 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
источник
очень хорошо! преимущество большого пальца перед принятым ответом состоит в том, что он не будет создавать комментарий XML, поэтому лучше работает для фрагмента XML
Умар Фарук Хаваджа
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();
}
Джонатан Митчем
источник
Это работает очень хорошо и не требует создания ненужных файлов на диске
Заин Ризви
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 без спецификации. Я нашел это сообщение в блоге и создал на его основе функцию:

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
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;
        }
    }
переписать
источник
Приведенный ниже ответ определенно может иметь какое-то объяснение, однако он сработал для меня и намного проще, чем другие решения.
CarlR
Кажется, вам нужно импортировать сборку 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игнорируется и всегда переопределяется кодировкой потока. При использовании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();
}
theJerm
источник
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(); 
}

Установка новой строки не требуется. Символы отступа также имеют два пробела по умолчанию, поэтому я предпочел не устанавливать его.

Dijoe
источник