Formatee una cadena XML para imprimir cadenas XML amigables

178

Tengo una cadena XML como tal:

<?xml version='1.0'?><response><error code='1'> Success</error></response>

No hay líneas entre un elemento y otro, y por lo tanto es muy difícil de leer. Quiero una función que formatee la cadena anterior:

<?xml version='1.0'?>
<response>
<error code='1'> Success</error>
</response> 

Sin recurrir a escribir manualmente la función de formato, ¿hay alguna biblioteca .Net o fragmento de código que pueda usar de forma informal?

Graviton
fuente
1
accesorios para CMS, la pregunta es un duplicado stackoverflow.com/questions/203528
Spence
2
No es un duplicado Ese especifica XmlDocumentcuál descalificaría la respuesta más votada en esta pregunta.
sirdank

Respuestas:

185

Use XmlTextWriter...

public static string PrintXML(string xml)
{
    string result = "";

    MemoryStream mStream = new MemoryStream();
    XmlTextWriter writer = new XmlTextWriter(mStream, Encoding.Unicode);
    XmlDocument document = new XmlDocument();

    try
    {
        // Load the XmlDocument with the XML.
        document.LoadXml(xml);

        writer.Formatting = Formatting.Indented;

        // Write the XML into a formatting XmlTextWriter
        document.WriteContentTo(writer);
        writer.Flush();
        mStream.Flush();

        // Have to rewind the MemoryStream in order to read
        // its contents.
        mStream.Position = 0;

        // Read MemoryStream contents into a StreamReader.
        StreamReader sReader = new StreamReader(mStream);

        // Extract the text from the StreamReader.
        string formattedXml = sReader.ReadToEnd();

        result = formattedXml;
    }
    catch (XmlException)
    {
        // Handle the exception
    }

    mStream.Close();
    writer.Close();

    return result;
}
SM Kamran
fuente
77
Esto funciona si se trata de código que está en una versión anterior de .NET Framework pre-LINQ, pero el otro ejemplo es mucho más limpio.
Mike
8
Para aclarar el comentario de Mike: LINQ se introdujo en .NET 3.5. Entonces, si está utilizando una versión de .NET más antigua que esa (.NET 1, 1.1, 2 o 3.0), tendrá que usar esta respuesta. Pero si está utilizando .NET 3.5 o posterior, la respuesta de Charles Prakash Dasari es mucho más simple.
Simon Tewsi
1
@SM Kamran, estoy usando su código, pero recibo un error que parece {{No se puede acceder a un flujo cerrado ".} En el escritor. Cerrar (); Por favor dar solución.
Jatin Gadhiya
@JatinGadhiya Tuve el mismo problema y lo resolví utilizando {using block} al definir las transmisiones. de esta manera, no necesita cerrar la transmisión manualmente y las transmisiones se cerrarán automáticamente cuando llegue al final del bloque de uso.
Vahid Farahmandian
312

Tendrá que analizar el contenido de alguna manera ... Creo que usar LINQ es la forma más fácil de hacerlo. Nuevamente, todo depende de su escenario exacto. Aquí hay un ejemplo de trabajo usando LINQ para formatear una cadena XML de entrada.

string FormatXml(string xml)
{
     try
     {
         XDocument doc = XDocument.Parse(xml);
         return doc.ToString();
     }
     catch (Exception)
     {
         // Handle and throw if fatal exception here; don't just ignore them
         return xml;
     }
 }

[el uso de declaraciones se omite por brevedad]

Charles Prakash Dasari
fuente
¿Esto afectará estrictamente los saltos de línea y la sangría? No quiero otros cambios, como "0" que se cambia a "0.0", etc. Cuando se eliminan todos los espacios en blanco, quiero que la cadena de resultados eliminada sea exactamente la misma que la cadena de entrada eliminada.
Radim Cernej
3
@radim Sí. No se realizarán cambios en los datos reales. Solo las etiquetas serán formateadas y sangradas.
Charles Prakash Dasari
2
Me di cuenta de que funcionaba bien con UTF8, pero no con el contenido del archivo Unicode XML.
Nayan
1
@SteveWellens, puede acceder a la declaración mediante doc.Declaration.ToString() + doc.ToString()o mediante el uso de en doc.Savelugar de doc.ToString. Vea este enlace para más detalles.
David French
1
Sugiera incluir los espacios de nombres, ya que evita que los usuarios tengan que buscar un espacio de nombres para una clase que quizás no hayan usado mucho antes. usando System.Xml.Linq; Funciona bien gracias!
Scott Moniz
61

Este, de kristopherjohnson, es mucho mejor:

  1. Tampoco requiere un encabezado de documento XML.
  2. Tiene excepciones más claras.
  3. Agrega opciones de comportamiento adicionales: OmitXmlDeclaration = true, NewLineOnAttributes = true
  4. Menos líneas de código

    static string PrettyXml(string xml)
    {
        var stringBuilder = new StringBuilder();
    
        var element = XElement.Parse(xml);
    
        var settings = new XmlWriterSettings();
        settings.OmitXmlDeclaration = true;
        settings.Indent = true;
        settings.NewLineOnAttributes = true;
    
        using (var xmlWriter = XmlWriter.Create(stringBuilder, settings))
        {
            element.Save(xmlWriter);
        }
    
        return stringBuilder.ToString();
    }
Todd
fuente
Todd, ¿podrías aclarar lo que quieres decir con "no requiere un encabezado de documento XML"? Probé la solución de Charles Prakash Dasari y acabo de pasar un fragmento XML sin una declaración xml (es decir, sin la <?xml version="1.0" encoding="UTF-8" ?>línea en la parte superior) y funcionó bien.
Simon Tewsi
3
En comparación con la respuesta aceptada. En comparación con Charles, este tendría una mejor configurabilidad. Sin embargo, probablemente usaría el método de Charlies en el futuro, tal configuración sería un requisito poco frecuente.
Todd
1
Este es mucho mejor y más corto
Alex Jolig
8

La solución simple que funciona para mí:

        XmlDocument xmlDoc = new XmlDocument();
        StringWriter sw = new StringWriter();
        xmlDoc.LoadXml(rawStringXML);
        xmlDoc.Save(sw);
        String formattedXml = sw.ToString();
ZeeProgrammer
fuente
esto crea un archivo xml con <? xml version = "1.0" encoding = "utf-16"?> como encabezado. XmlSerializer no analizó esto con el error 'No hay marca de orden de bytes Unicode'. La solución fue eliminar la codificación = "utf-16", ver: stackoverflow.com/questions/29915467/… .
Declan Taylor
6

Verifique el siguiente enlace: Cómo imprimir XML bonito (Desafortunadamente, el enlace ahora devuelve 404 :()

El método en el enlace toma una cadena XML como argumento y devuelve una cadena XML bien formada (sangrada).

Acabo de copiar el código de muestra del enlace para que esta respuesta sea más completa y conveniente.

public static String PrettyPrint(String XML)
{
    String Result = "";

    MemoryStream MS = new MemoryStream();
    XmlTextWriter W = new XmlTextWriter(MS, Encoding.Unicode);
    XmlDocument D   = new XmlDocument();

    try
    {
        // Load the XmlDocument with the XML.
        D.LoadXml(XML);

        W.Formatting = Formatting.Indented;

        // Write the XML into a formatting XmlTextWriter
        D.WriteContentTo(W);
        W.Flush();
        MS.Flush();

        // Have to rewind the MemoryStream in order to read
        // its contents.
        MS.Position = 0;

        // Read MemoryStream contents into a StreamReader.
        StreamReader SR = new StreamReader(MS);

        // Extract the text from the StreamReader.
        String FormattedXML = SR.ReadToEnd();

        Result = FormattedXML;
    }
    catch (XmlException)
    {
    }

    MS.Close();
    W.Close();

    return Result;
}
Chansik Im
fuente
2
Funciona muy bien para mí, solo lo convertí en un método de extensión de cadena. Además, ese sitio web no funciona, así que es bueno que hayas conseguido una copia ...
goodguys_activate
1
Respuesta duplicada @SM Kamran publica la misma respuesta también.
Vahid Farahmandian
@VahidFarahmandian Sí. No pude hacer mucho al respecto porque publiqué 1 minuto antes que él :) Por cierto, estaba tratando de agregar de dónde vino la respuesta para dar crédito al póster del blog. Desafortunadamente, el enlace está roto ahora :(.
Chansik Im
Me gusta esta respuesta la mejor en comparación con la de Charles (FormatXml) y Todd (PrettyXml), porque esta respuesta no elimina la <?xml...?>línea. Esta respuesta obtiene lo que originalmente tenía en mente. Lo único negativo sería que preferiría tabulaciones en lugar de espacios utilizados de forma nativa. Me puse Indentation = 1y IndentChar = '\t'para obtener exactamente lo que quería.
Sarah Weinberger
@ CHICoder007 Gracias por el comentario sobre el método de extensión. Me enseñaste algo nuevo. Agregar en una (this String XML)gran obra.
Sarah Weinberger
4

Lo intenté:

internal static void IndentedNewWSDLString(string filePath)
{
    var xml = File.ReadAllText(filePath);
    XDocument doc = XDocument.Parse(xml);
    File.WriteAllText(filePath, doc.ToString());
}

Está funcionando bien como se esperaba.

Akhilesh singh
fuente
pero esto elimina la etiqueta <? xml?> en la parte superior
Juran
2

.NET 2.0 ignorando la resolución de nombres y con la disposición adecuada de recursos, sangría, preservar espacios en blanco y codificación personalizada :

public static string Beautify(System.Xml.XmlDocument doc)
{
    string strRetValue = null;
    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    // enc = new System.Text.UTF8Encoding(false);

    System.Xml.XmlWriterSettings xmlWriterSettings = new System.Xml.XmlWriterSettings();
    xmlWriterSettings.Encoding = enc;
    xmlWriterSettings.Indent = true;
    xmlWriterSettings.IndentChars = "    ";
    xmlWriterSettings.NewLineChars = "\r\n";
    xmlWriterSettings.NewLineHandling = System.Xml.NewLineHandling.Replace;
    //xmlWriterSettings.OmitXmlDeclaration = true;
    xmlWriterSettings.ConformanceLevel = System.Xml.ConformanceLevel.Document;


    using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
    {
        using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(ms, xmlWriterSettings))
        {
            doc.Save(writer);
            writer.Flush();
            ms.Flush();

            writer.Close();
        } // End Using writer

        ms.Position = 0;
        using (System.IO.StreamReader sr = new System.IO.StreamReader(ms, enc))
        {
            // Extract the text from the StreamReader.
            strRetValue = sr.ReadToEnd();

            sr.Close();
        } // End Using sr

        ms.Close();
    } // End Using ms


    /*
    System.Text.StringBuilder sb = new System.Text.StringBuilder(); // Always yields UTF-16, no matter the set encoding
    using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(sb, settings))
    {
        doc.Save(writer);
        writer.Close();
    } // End Using writer
    strRetValue = sb.ToString();
    sb.Length = 0;
    sb = null;
    */

    xmlWriterSettings = null;
    return strRetValue;
} // End Function Beautify

Uso:

System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.XmlResolver = null;
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("C:\Test.svg");
string SVG = Beautify(xmlDoc);
Stefan Steiger
fuente
0

si carga el XMLDoc, estoy bastante seguro de que la función .ToString () posee una sobrecarga para esto.

¿Pero es esto para depurar? La razón por la que se envía así es para ocupar menos espacio (es decir, eliminar espacios en blanco innecesarios del XML).

Spence
fuente
0

Salida Pretty XML personalizable con declaración XML UTF-8

La siguiente definición de clase proporciona un método simple para convertir una cadena XML de entrada en XML formateado de salida con la declaración xml como UTF-8. Admite todas las opciones de configuración que ofrece la clase XmlWriterSettings .

using System;
using System.Text;
using System.Xml;
using System.IO;

namespace CJBS.Demo
{
    /// <summary>
    /// Supports formatting for XML in a format that is easily human-readable.
    /// </summary>
    public static class PrettyXmlFormatter
    {

        /// <summary>
        /// Generates formatted UTF-8 XML for the content in the <paramref name="doc"/>
        /// </summary>
        /// <param name="doc">XmlDocument for which content will be returned as a formatted string</param>
        /// <returns>Formatted (indented) XML string</returns>
        public static string GetPrettyXml(XmlDocument doc)
        {
            // Configure how XML is to be formatted
            XmlWriterSettings settings = new XmlWriterSettings 
            {
                Indent = true
                , IndentChars = "  "
                , NewLineChars = System.Environment.NewLine
                , NewLineHandling = NewLineHandling.Replace
                //,NewLineOnAttributes = true
                //,OmitXmlDeclaration = false
            };

            // Use wrapper class that supports UTF-8 encoding
            StringWriterWithEncoding sw = new StringWriterWithEncoding(Encoding.UTF8);

            // Output formatted XML to StringWriter
            using (XmlWriter writer = XmlWriter.Create(sw, settings))
            {
                doc.Save(writer);
            }

            // Get formatted text from writer
            return sw.ToString();
        }



        /// <summary>
        /// Wrapper class around <see cref="StringWriter"/> that supports encoding.
        /// Attribution: http://stackoverflow.com/a/427737/3063884
        /// </summary>
        private sealed class StringWriterWithEncoding : StringWriter
        {
            private readonly Encoding encoding;

            /// <summary>
            /// Creates a new <see cref="PrettyXmlFormatter"/> with the specified encoding
            /// </summary>
            /// <param name="encoding"></param>
            public StringWriterWithEncoding(Encoding encoding)
            {
                this.encoding = encoding;
            }

            /// <summary>
            /// Encoding to use when dealing with text
            /// </summary>
            public override Encoding Encoding
            {
                get { return encoding; }
            }
        }
    }
}

Posibilidades de mejora adicional: -

  • Se GetPrettyXml(XmlDocument doc, XmlWriterSettings settings)podría crear un método adicional que permita a la persona que llama personalizar la salida.
  • Se GetPrettyXml(String rawXml)podría agregar un método adicional que admita el análisis de texto sin formato, en lugar de que el cliente use XmlDocument. En mi caso, necesitaba manipular el XML usando XmlDocument, por lo tanto, no agregué esto.

Uso:

String myFormattedXml = null;
XmlDocument doc = new XmlDocument();
try
{
    doc.LoadXml(myRawXmlString);
    myFormattedXml = PrettyXmlFormatter.GetPrettyXml(doc);
}
catch(XmlException ex)
{
    // Failed to parse XML -- use original XML as formatted XML
    myFormattedXml = myRawXmlString;
}
CJBS
fuente