Qual é a maneira mais simples de obter XML recuado com quebras de linha de XmlDocument?


105

Quando eu construo XML do zero XmlDocument, a OuterXmlpropriedade já tem tudo muito bem recuado com quebras de linha. No entanto, se eu chamar LoadXmlalgum XML muito "compactado" (sem quebras de linha ou recuo), a saída de OuterXmlpermanecerá dessa forma. Assim ...

Qual é a maneira mais simples de obter uma saída XML embelezada de uma instância de XmlDocument?

Respostas:


209

Com base nas outras respostas, pesquisei XmlTextWritere descobri o seguinte método auxiliar:

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

É um pouco mais código do que eu esperava, mas funciona perfeitamente.


5
Você pode até considerar a criação de seu método de utilitário como um método de extensão para a classe XmlDocument.
Oposição

5
Curiosamente, para mim, isso não faz nada, exceto definir a codificação do cabeçalho xml para UTF-16. Estranhamente, ele faz isso mesmo se eu definir explicitamentesettings.Encoding = Encoding.UTF8;
Nyerguds

3
O problema de codificação pode ser resolvido usando um MemoryStream+ StreamWritercom uma codificação especificada em vez de StringBuilder, e obtendo o texto com enc.GetString(memstream.GetBuffer(), 0, (int)memstream.Length);. O resultado final ainda não está formatado de forma alguma. Será que estou partindo de um documento lido que já tem formatação? Eu só quero que meus novos nós sejam formatados também.
Nyerguds

2
Estou tentado a modificar o "\r\n"para Environment.Newline.
Pharap

2
doc.PreserveWhitespacenão deve ser definido como verdadeiro. Caso contrário, ele falhará se já contiver recuo parcial.
Mestre DJ em

48

Conforme adaptado do blog de Erika Ehrli , isso deve bastar :

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
o fechamento da usinginstrução fechará automaticamente o escritor quando Dispose()for chamado.
Tyler Lee

3
Para mim, isso só recua uma linha. Ainda tenho dezenas de outras linhas que não estão recuadas.
C Johnson

40

Ou ainda mais fácil se você tiver acesso ao 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);
}

muito agradável! A vantagem positiva sobre a resposta aceita é que ele não produzirá um comentário XML, então funciona melhor para um fragmento XML
Umar Farooq Khawaja

3
Estranhamente, isso remove o <?xml ...?>e o <!DOCTYPE ...>do XML. OK para um fragmento, mas não desejável para um documento completo.
Jesse Chisholm

Esta é a única maneira que funcionou para mim. Todos os outros métodos usando xmltextwriter, Formatting = Formatting.Indented e XmlWriterSettings NÃO reformata o texto, mas esse método sim.
kexx

16

Uma versão de método de extensão mais curta

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

Isso funciona muito bem e não envolve a criação de arquivos desnecessários no disco
Zain Rizvi

13

Se o método Beautify acima estiver sendo chamado para um XmlDocumentque já contém um XmlProcessingInstructionnó filho, a seguinte exceção é lançada:

Não é possível gravar a declaração XML. O método WriteStartDocument já o escreveu.

Esta é minha versão modificada da original para me livrar da exceção:

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 mim agora, provavelmente você precisaria verificar todos os nós filhos para o XmlProcessingInstructionnó, não apenas o primeiro?


Atualização em abril de 2015:

Como tive outro caso em que a codificação estava errada, procurei como aplicar o UTF-8 sem BOM. Encontrei esta postagem do blog e criei uma função com base nela:

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

não funcionará se você colocar a seção cdata dentro do nó pai e antes do nó filho
Sasha Bond

2
MemoryStream não parece ser necessário, pelo menos do meu lado. Nas configurações eu configurei: Encoding = Encoding.UTF8eOmitXmlDeclaration = true
Mestre 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;
        }
    }

A resposta abaixo definitivamente poderia ter alguma explicação, mas funcionou para mim e é muito mais simples do que as outras soluções.
CarlR

Parece que você precisa importar o assembly system.link.XML para que funcione no PS 3.
CarlR

2

Uma maneira simples é usar:

writer.WriteRaw(space_char);

Como este código de amostra, este código é o que eu usei para criar uma estrutura em forma de árvore 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();

            }

        }

Desta forma, você pode adicionar quebras de tabulação ou linha da maneira que está acostumado, ou seja, \ t ou \ n


1

Ao implementar as sugestões postadas aqui, tive problemas com a codificação do texto. Parece que a codificação de XmlWriterSettingsé ignorada e sempre substituída pela codificação do fluxo. Ao usar um StringBuilder, esta é sempre a codificação de texto usada internamente em C #, ou seja, UTF-16.

Portanto, aqui está uma versão que também oferece suporte a outras codificações.

NOTA IMPORTANTE: A formatação é completamente ignorada se o seu XMLDocumentobjeto preserveWhitespaceestiver com sua propriedade habilitada ao carregar o documento. Isso me deixou perplexo por um tempo, então certifique-se de não habilitar isso.

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

Isso salvará o xml formatado no disco, com a codificação de texto fornecida.


1

Se você tiver uma string de XML, em vez de um documento pronto para uso, pode fazer desta forma:

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

Uma abordagem mais simplificada com base na resposta aceita:

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

Não é necessário definir a nova linha. Caracteres de recuo também têm os dois espaços padrão, portanto, preferi não defini-los também.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.