Formate a sequência XML para imprimir uma sequência XML amigável

178

Eu tenho uma string XML como tal:

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

Não há linhas entre um elemento e outro e, portanto, é muito difícil de ler. Eu quero uma função que formata a seqüência acima:

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

Sem recorrer a escrever manualmente a função de formato, existe alguma biblioteca .Net ou trecho de código que eu possa usar de antemão?

Graviton
fonte
1
adereços para CMS, a pergunta é uma duplicata stackoverflow.com/questions/203528
Spence
2
Não é uma duplicata. Aquele especifica XmlDocumentqual desqualificaria a resposta mais votada nesta questão.
precisa saber é o seguinte

Respostas:

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
fonte
7
Isso funciona se você estiver lidando com código que está em uma versão antiga do .NET framework pré-LINQ, mas o outro exemplo é muito mais limpo.
Mike
8
Para esclarecer o comentário de Mike: O LINQ foi introduzido no .NET 3.5. Portanto, se você estiver usando uma versão do .NET mais antiga (.NET 1, 1.1, 2 ou 3.0), precisará usar esta resposta. Mas se você estiver usando o .NET 3.5 ou posterior, a resposta de Charles Prakash Dasari é muito mais simples.
Simon Tewsi
1
@SM Kamran, estou usando seu código, mas estou recebendo um erro parecido com {"Não é possível acessar um fluxo fechado."} Em writer.Close (); pls dar solução.
Jatin Gadhiya
@JatinGadhiya Eu tive o mesmo problema e resolvi usando {using block} na definição dos fluxos. dessa forma, você não precisa fechar o fluxo manualmente e os fluxos serão fechados automaticamente ao chegar ao final do bloco de uso.
Vahid Farahmandian
312

Você precisará analisar o conteúdo de alguma forma ... Acho que usar o LINQ é a maneira mais fácil de fazer isso. Novamente, tudo depende do seu cenário exato. Aqui está um exemplo de trabalho usando o LINQ para formatar uma string 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;
     }
 }

[instruções usando são omitidas por questões de brevidade]

Charles Prakash Dasari
fonte
Isso afetará estritamente quebras de linha e indentação? Não quero nenhuma outra alteração, como "0" sendo alterado para "0,0" etc. Quando todo o espaço em branco for removido, quero que a sequência de resultados removida seja exatamente a mesma que a sequência de entrada removida.
Radim Cernej
3
@radim Sim. Nenhuma alteração nos dados reais será feita. Somente as tags serão formatadas e recuadas.
Charles Prakash Dasari
2
Percebi que funcionou bem com o UTF8, mas não com o conteúdo do arquivo XML Unicode.
Nayan
1
@SteveWellens, você pode acessar a declaração via doc.Declaration.ToString() + doc.ToString()ou usando em doc.Savevez de doc.ToString. Veja este link para mais detalhes.
David French
1
Sugira incluir os namespaces, pois isso evita que os usuários procurem um namespace para uma classe que talvez não tenham usado muito antes. using System.Xml.Linq; Funciona bem Obrigado!
Scott Moniz
61

Este, de kristopherjohnson, é muito melhor:

  1. Também não requer um cabeçalho de documento XML.
  2. Tem exceções mais claras
  3. Adiciona opções adicionais de comportamento: OmitXmlDeclaration = true, NewLineOnAttributes = true
  4. Menos linhas 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
fonte
Todd, você poderia esclarecer o que quer dizer com "não requer um cabeçalho de documento XML"? Eu tentei a solução de Charles Prakash Dasari e passei um fragmento XML sem uma declaração xml (ou seja, sem a <?xml version="1.0" encoding="UTF-8" ?>linha na parte superior) e funcionou bem.
Simon Tewsi 5/09/2013
3
Comparado com a resposta aceita. Comparado a Charles, este teria melhor configurabilidade. No entanto, eu provavelmente usaria o método Charlies no futuro, essa configuração seria um requisito raro.
21413 Todd
1
Este é muito, muito melhor e mais curto
Alex Jolig
8

A solução simples que está funcionando para mim:

        XmlDocument xmlDoc = new XmlDocument();
        StringWriter sw = new StringWriter();
        xmlDoc.LoadXml(rawStringXML);
        xmlDoc.Save(sw);
        String formattedXml = sw.ToString();
ZeeProgrammer
fonte
isso cria um arquivo xml com <? xml version = "1.0" encoding = "utf-16"?> como cabeçalho. Isso não foi analisado pelo XmlSerializer, com o erro 'Não há marca de ordem de bytes Unicode'. A correção foi remover o encoding = "utf-16", consulte: stackoverflow.com/questions/29915467/… .
Declan Taylor
6

Verifique o link a seguir: Como imprimir XML de maneira bonita (Infelizmente, o link agora retorna 404 :()

O método no link usa uma string XML como argumento e retorna uma string XML bem formada (recuada).

Acabei de copiar o código de exemplo do link para tornar essa resposta mais abrangente e 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
fonte
2
Funciona muito bem para mim, acabei de torná-lo um método de extensão de string. Também que o site está em baixo, por isso é bom que você agarrou uma cópia ...
goodguys_activate
1
Resposta duplicada. @SM Kamran também publica a mesma resposta.
Vahid Farahmandian
@VahidFarahmandian Yes. Não pude fazer muita coisa porque publiquei 1 minuto antes dele :) BTW, estava tentando acrescentar de onde veio a resposta para dar crédito ao pôster do blog. Infelizmente, o link está quebrado agora :(.
Chansik Im 13/04/16
Eu gosto desta resposta da melhor forma que a de Charles (FormatXml) e Todd (PrettyXml), porque essa resposta não tira a <?xml...?>linha. Esta resposta tem o que eu originalmente tinha em mente. O único aspecto negativo seria que eu preferiria tabulações em vez de espaços usados ​​nativamente. Armei Indentation = 1e IndentChar = '\t'consegui exatamente o que queria.
Sarah Weinberger
@ CHICoder007 Obrigado pelo comentário sobre o método de extensão. Você me ensinou algo novo. Adicionando um (this String XML)funciona muito bem.
Sarah Weinberger
4

Eu tentei:

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

está funcionando bem como esperado.

Akhilesh singh
fonte
mas isso remove a tag <? xml?> na parte superior
Juran
2

O .NET 2.0 ignora a resolução de nomes e com a disposição adequada de recursos, recuo, espaço em branco para preservar e codificação 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
fonte
0

se você carregar o XMLDoc, tenho certeza de que a função .ToString () possui uma sobrecarga para isso.

Mas isso é para depuração? O motivo pelo qual é enviado dessa maneira é ocupar menos espaço (ou seja, removendo o espaço em branco desnecessário do XML).

Spence
fonte
0

Saída Pretty XML personalizável com declaração XML UTF-8

A seguinte definição de classe fornece um método simples para converter uma sequência XML de entrada em XML de saída formatada com a declaração xml como UTF-8. Ele suporta todas as opções de configuração que a classe XmlWriterSettings oferece.

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

Possibilidades de melhorias adicionais: -

  • Um método adicional GetPrettyXml(XmlDocument doc, XmlWriterSettings settings)pode ser criado para permitir que o chamador personalize a saída.
  • Um método adicional GetPrettyXml(String rawXml)pode ser adicionado para oferecer suporte à análise de texto bruto, em vez de o cliente usar o XmlDocument. No meu caso, eu precisava manipular o XML usando o XmlDocument, portanto, não o adicionei.

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
fonte