¿Cuál es la forma más simple de obtener sangría XML con saltos de línea de XmlDocument?

Cuando construyo XML desde cero con XmlDocument , la propiedad OuterXml ya tiene todo sangrado con saltos de línea. Sin embargo, si llamo a LoadXml en un XML muy “comprimido” (sin saltos de línea o sangría), entonces la salida de OuterXml mantiene de esa manera. Asi que …

¿Cuál es la forma más sencilla de obtener un resultado XML embellecido de una instancia de XmlDocument ?

En función de las otras respuestas, busqué en XmlTextWriter y XmlTextWriter el siguiente método de ayuda:

 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 un poco más código de lo que esperaba, pero funciona de maravilla.

Según la adaptación del blog de Erika Ehrli , esto debería hacerlo:

 XmlDocument doc = new XmlDocument(); doc.LoadXml("wrench"); // 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); } 

O incluso más fácil si tienes acceso a 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); } 

Una versión más corta del método de extensió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(); } 

Si se llama al método Beautify anterior para un XmlDocument que ya contiene un nodo secundario XmlProcessingInstruction se lanza la siguiente excepción:

No se puede escribir la statement XML. Método WriteStartDocument ya lo ha escrito.

Esta es mi versión modificada del original para deshacerme de la excepción:

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

Funciona para mí ahora, probablemente necesitaría escanear todos los nodos secundarios para el nodo XmlProcessingInstruction , ¿no solo el primero?


Actualización de abril de 2015:

Como tenía otro caso en el que la encoding era incorrecta, busqué cómo aplicar UTF-8 sin BOM. Encontré esta publicación de blog y creé una función basada en ella:

 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; } } 
 XmlTextWriter xw = new XmlTextWriter(writer); xw.Formatting = Formatting.Indented; 
  public static string FormatXml(string xml) { try { var doc = XDocument.Parse(xml); return doc.ToString(); } catch (Exception) { return xml; } } 

Una forma simple es usar:

 writer.WriteRaw(space_char); 

Al igual que este código de muestra, este código es lo que utilicé para crear una estructura de vista de árbol usando 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(); } } 

De esta forma, puede agregar tabulaciones o saltos de línea de la forma en que normalmente está acostumbrado, es decir, \ t o \ n

Al implementar las sugerencias publicadas aquí, tuve problemas con la encoding de texto. Parece que la encoding de XmlWriterSettings se ignora, y siempre se reemplaza por la encoding de la secuencia. Cuando se utiliza un StringBuilder , esta es siempre la encoding de texto utilizada internamente en C #, es decir, UTF-16.

Así que aquí hay una versión que también admite otras codificaciones.

NOTA IMPORTANTE: El formato se ignora por completo si su objeto XMLDocument tiene habilitada su propiedad preserveWhitespace al cargar el documento. Esto me dejó perplejo por un tiempo, así que asegúrate de no habilitar eso.

Mi código final:

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

Esto guardará el xml formateado en el disco, con la encoding de texto dada.

Si tiene una cadena de XML, en lugar de un documento listo para usar, puede hacerlo de esta manera:

 var xmlString = "..."; // 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(); } 

Un enfoque más simplificado basado en la respuesta aceptada:

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

Establecer la nueva línea no es necesario. Los caracteres de sangría también tienen los dos espacios predeterminados, así que prefiero no configurarlo también.