XDocument.ToString () descarta a tag de codificação XML

103

Existe alguma maneira de obter a codificação xml na função toString ()?

Exemplo:

xml.Save("myfile.xml");

leva a

<?xml version="1.0" encoding="utf-8"?>
<Cooperations>
  <Cooperation>
    <CooperationId>xxx</CooperationId>
    <CooperationName>Allianz Konzern</CooperationName>
    <LogicalCustomers>

Mas

tb_output.Text = xml.toString();

leva a uma saída como esta

<Cooperations>
  <Cooperation>
    <CooperationId>xxx</CooperationId>
    <CooperationName>Allianz Konzern</CooperationName>
    <LogicalCustomers>
    ...
Henrik P. Hessel
fonte

Respostas:

98

Escreva explicitamente a declaração ou use um StringWritere chame Save():

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

class Test
{
    static void Main()
    {
        string xml = @"<?xml version='1.0' encoding='utf-8'?>
<Cooperations>
  <Cooperation />
</Cooperations>";

        XDocument doc = XDocument.Parse(xml);
        StringBuilder builder = new StringBuilder();
        using (TextWriter writer = new StringWriter(builder))
        {
            doc.Save(writer);
        }
        Console.WriteLine(builder);
    }
}

Você pode facilmente adicionar isso como um método de extensão:

public static string ToStringWithDeclaration(this XDocument doc)
{
    if (doc == null)
    {
        throw new ArgumentNullException("doc");
    }
    StringBuilder builder = new StringBuilder();
    using (TextWriter writer = new StringWriter(builder))
    {
        doc.Save(writer);
    }
    return builder.ToString();
}

Isso tem a vantagem de não explodir se não houver uma declaração :)

Então você pode usar:

string x = doc.ToStringWithDeclaration();

Observe que usará utf-16 como a codificação, porque essa é a codificação implícita em StringWriter. Você mesmo pode influenciar isso criando uma subclasse de StringWriter, por exemplo, sempre usar UTF-8 .

Jon Skeet
fonte
14
Isso tem um pequeno problema, pois a codificação na declaração XDocument é ignorada e substituída pela codificação do StringWriter ao salvar, que pode ou não ser o que você deseja
Sam Holder
2
Em seguida, você combina o método de extensão com: Utf8StringWriter de stackoverflow.com/a/1564727/75963 ;)
Nick Josevski
12
Achei mais fácil usar o método de extensão acima, mas retornar o seguinte ... return doc.Declaration + doc.ToString (); Se a declaração for nula, resultará apenas em uma string vazia.
Steve G.
Estranho, mas não consigo fazer funcionar agora ( fiddle .net ) - ele sempre usa a codificação "utf-16". Eu olhei dentro da XDocument.Save(TextWriter)implementação e ele simplesmente ignora a codificação da declaração, em oposição a XDocument.Save(String)ou XDocument.Save(Stream)implementações. Eu me pergunto por que ...
Ilya Luzyanin
@IlyaLuzyanin: Sim, ele usará "utf-16" como a codificação quando você passar um StringWriter, a menos que você use um que substitua a Encodingpropriedade. Eu tenho outra resposta sobre isso. Achei que você estava dizendo que a "codificação" estava descartada totalmente ...
Jon Skeet
46

A propriedade Declaração conterá a declaração XML. Para obter o conteúdo mais a declaração, você pode fazer o seguinte:

tb_output.Text = xml.Declaration.ToString() + xml.ToString()
Ryan Brunner
fonte
7
parece que se você não usar new XDeclaration ("1.0", "utf-8", "yes") em seu xdocument, isso criará um erro porque xml.Declaration é nulo. Mas xml.save parece detectar automaticamente a codificação correta.
Henrik P. Hessel
outb_output.Text = @"<?xml version=""1.0"" encoding=""utf-8"" ?>" + xml;
Bill Hoag
4
ou... = $"{xdoc.Declaration}{Environment.NewLine}{xdoc}";
WernerCD
9

usa isto:

output.Text = String.Concat(xml.Declaration.ToString() , xml.ToString())
Farooq Kaiser
fonte
2
Sem criar um novo XDeclaration ("1.0", "utf-8", "yes") e adicionar em XDocument ou outro objeto, xml.Declaration.ToString () lançará uma exceção nula.
Ziggler
1
é mais seguro como abaixo porque o Concat não se preocupa com strings nulas: output.Text = String.Concat (xml.Declaration, xml)
dmihailescu
3

Eu gostei disso

        string distributorInfo = string.Empty;

        XDocument distributors = new XDocument();

     //below is important else distributors.Declaration.ToString() throws null exception
        distributors.Declaration = new XDeclaration("1.0", "utf-8", "yes"); 

        XElement rootElement = new XElement("Distributors");
        XElement distributor = null;
        XAttribute id = null;

        distributor = new XElement("Distributor");
        id = new XAttribute("Id", "12345678");
        distributor.Add(id);
        rootElement.Add(distributor);

        distributor = new XElement("Distributor");
        id = new XAttribute("Id", "22222222");

        distributor.Add(id);

        rootElement.Add(distributor);         

        distributors.Add(rootElement);

        distributorInfo = String.Concat(distributors.Declaration.ToString(), distributors.ToString());

Veja abaixo o que recebo em distributorInfo

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Distributors>
  <Distributor Id="12345678" />
  <Distributor Id="22222222" />
  <Distributor Id="11111111" />
</Distributors>
Ziggler
fonte
1
bom exemplo. algumas notas: 1) use novo XDeclaration ("1.0", "utf-8") ao invés de novo XDeclaration ("1.0", "utf-8", "sim"), 2) insira uma nova linha na última linha: distribuidores. Declaração.ToString () + Environment.NewLine + distributors.ToString ()
Alexey Obukhov
2

Semelhante às outras respostas +1, mas com um pouco mais de detalhes sobre a declaração e uma concatenação um pouco mais precisa.

<xml />declaração deve estar em sua própria linha em um XML formatado, portanto, estou garantindo que adicionamos a nova linha. NOTA: usar Environment.Newlineassim produzirá a nova linha específica da plataforma

// Parse xml declaration menthod
XDocument document1 =
  XDocument.Parse(@"<?xml version=""1.0"" encoding=""iso-8859-1""?><rss version=""2.0""></rss>");
string result1 =
  document1.Declaration.ToString() +
  Environment.NewLine +
  document1.ToString() ;

// Declare xml declaration method
XDocument document2 = 
  XDocument.Parse(@"<rss version=""2.0""></rss>");
document2.Declaration =
  new XDeclaration("1.0", "iso-8859-1", null);
string result2 =
  document2.Declaration.ToString() +
  Environment.NewLine +
  document2.ToString() ;

Ambos os resultados produzem:

<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0"></rss>
Sonjz
fonte
1

Algumas dessas respostas atendem ao pedido do autor da postagem, mas parecem complicadas demais. Aqui está um método de extensão simples que evita a necessidade de um gravador separado, lida com uma declaração ausente e oferece suporte ao parâmetro padrão ToString SaveOptions.

public static string ToXmlString(this XDocument xdoc, SaveOptions options = SaveOptions.None)
{
    var newLine =  (options & SaveOptions.DisableFormatting) == SaveOptions.DisableFormatting ? "" : Environment.NewLine;
    return xdoc.Declaration == null ? xdoc.ToString(options) : xdoc.Declaration + newLine + xdoc.ToString(options);
}

Para usar a extensão, basta substituir xml.ToString()porxml.ToXmlString()

B2K
fonte
0

Você também pode usar um XmlWriter e chamar o

Writer.WriteDocType() 

método.

Gus Paul
fonte
0
string uploadCode = "UploadCode";
string LabName = "LabName";
XElement root = new XElement("TestLabs");
foreach (var item in returnList)
{  
       root.Add(new XElement("TestLab",
                new XElement(uploadCode, item.UploadCode),
                new XElement(LabName, item.LabName)
                            )
               );
}

XDocument returnXML = new XDocument(new XDeclaration("1.0", "UTF-8","yes"),
             root);

string returnVal;
using (var sw = new MemoryStream())
{
       using (var strw = new StreamWriter(sw, System.Text.UTF8Encoding.UTF8))
       {
              returnXML.Save(strw);
              returnVal = System.Text.UTF8Encoding.UTF8.GetString(sw.ToArray());
       }
}

// ReturnVal has the string with XML data with XML declaration tag
David
fonte