Usando StringWriter para serialização de XML

Atualmente estou procurando uma maneira fácil de serializar objects (em C # 3).

Eu pesquisei alguns exemplos e encontrei algo como:

MemoryStream memoryStream = new MemoryStream ( ); XmlSerializer xs = new XmlSerializer ( typeof ( MyObject) ); XmlTextWriter xmlTextWriter = new XmlTextWriter ( memoryStream, Encoding.UTF8 ); xs.Serialize ( xmlTextWriter, myObject); string result = Encoding.UTF8.GetString(memoryStream .ToArray()); 

Depois de ler esta pergunta , perguntei a mim mesmo: por que não usar o StringWriter? Parece muito mais fácil.

 XmlSerializer ser = new XmlSerializer(typeof(MyObject)); StringWriter writer = new StringWriter(); ser.Serialize(writer, myObject); serializedValue = writer.ToString(); 

Outro problema foi que o primeiro exemplo gerado XML eu não poderia simplesmente escrever em uma coluna XML do database do SQL Server 2005.

A primeira pergunta é: há uma razão pela qual eu não deveria usar StringWriter para serializar um object quando eu precisar dele como uma string depois? Eu nunca encontrei um resultado usando StringWriter quando googling.

A segunda é, claro: se você não deveria fazê-lo com StringWriter (por qualquer motivo), qual seria uma maneira boa e correta?


Adição:

Como já foi mencionado por ambas as respostas, vou mais adiante no problema XML para DB.

Ao gravar no database, recebi a seguinte exceção:

System.Data.SqlClient.SqlException: análise XML: linha 1, caractere 38, incapaz de alternar a codificação

Para string

  

Eu peguei a string criada a partir do XmlTextWriter e coloquei como xml lá. Este não funcionou (nem com inserção manual no DB).

Depois tentei inserção manual (apenas escrevendo INSERT INTO …) com encoding = “utf-16” que também falhou. Removendo a codificação totalmente funcionou então. Depois desse resultado eu mudei de volta para o código StringWriter e voila – funcionou.

Problema: Eu não entendo o porque.

em Christian Hayter: Com esses testes eu não tenho certeza se tenho que usar o utf-16 para escrever no DB. Não definir a codificação para o trabalho UTF-16 (na tag xml)?

Ao serializar um documento XML em uma cadeia .NET, a codificação deve ser definida como UTF-16. As strings são armazenadas como UTF-16 internamente, portanto, essa é a única codificação que faz sentido. Se você quiser armazenar dados em uma codificação diferente, use uma matriz de bytes.

O SQL Server funciona em um princípio semelhante; qualquer string passada para uma coluna xml deve ser codificada como UTF-16. O SQL Server rejeitará qualquer sequência em que a declaração XML não especifique UTF-16. Se a declaração XML não estiver presente, o padrão XML exigirá que o padrão seja UTF-8, portanto, o SQL Server também a rejeitará.

Tendo isso em mente, aqui estão alguns methods utilitários para fazer a conversão.

 public static string Serialize(T value) { if(value == null) { return null; } XmlSerializer serializer = new XmlSerializer(typeof(T)); XmlWriterSettings settings = new XmlWriterSettings() { Encoding = new UnicodeEncoding(false, false), // no BOM in a .NET string Indent = false, OmitXmlDeclaration = false }; using(StringWriter textWriter = new StringWriter()) { using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) { serializer.Serialize(xmlWriter, value); } return textWriter.ToString(); } } public static T Deserialize(string xml) { if(string.IsNullOrEmpty(xml)) { return default(T); } XmlSerializer serializer = new XmlSerializer(typeof(T)); XmlReaderSettings settings = new XmlReaderSettings(); // No settings need modifying here using(StringReader textReader = new StringReader(xml)) { using(XmlReader xmlReader = XmlReader.Create(textReader, settings)) { return (T) serializer.Deserialize(xmlReader); } } } 

Um problema com StringWriter é que, por padrão, ele não permite que você defina a codificação que ele anuncia – assim você pode acabar com um documento XML anunciando sua codificação como UTF-16, o que significa que você precisa codificá-lo como UTF-16 se você escreve em um arquivo. Eu tenho uma pequena turma para ajudar com isso:

 public sealed class StringWriterWithEncoding : StringWriter { public override Encoding Encoding { get; } public StringWriterWithEncoding (Encoding encoding) { Encoding = encoding; } } 

Ou se você só precisa de UTF-8 (que é tudo o que eu preciso):

 public sealed class Utf8StringWriter : StringWriter { public override Encoding Encoding => Encoding.UTF8; } 

Quanto ao motivo pelo qual você não pôde salvar seu XML no database – você terá que nos dar mais detalhes sobre o que aconteceu quando você tentou, se você quer que possamos diagnosticá-lo / consertá-lo.

Antes de mais nada, cuidado para encontrar exemplos antigos. Você encontrou um que usa XmlTextWriter , que é obsoleto a partir do .NET 2.0. XmlWriter.Create deve ser usado em vez disso.

Veja um exemplo de serialização de um object em uma coluna XML:

 public void SerializeToXmlColumn(object obj) { using (var outputStream = new MemoryStream()) { using (var writer = XmlWriter.Create(outputStream)) { var serializer = new XmlSerializer(obj.GetType()); serializer.Serialize(writer, obj); } outputStream.Position = 0; using (var conn = new SqlConnection(Settings.Default.ConnectionString)) { conn.Open(); const string INSERT_COMMAND = @"INSERT INTO XmlStore (Data) VALUES (@Data)"; using (var cmd = new SqlCommand(INSERT_COMMAND, conn)) { using (var reader = XmlReader.Create(outputStream)) { var xml = new SqlXml(reader); cmd.Parameters.Clear(); cmd.Parameters.AddWithValue("@Data", xml); cmd.ExecuteNonQuery(); } } } } } 
 public static T DeserializeFromXml(string xml) { T result; XmlSerializerFactory serializerFactory = new XmlSerializerFactory(); XmlSerializer serializer =serializerFactory.CreateSerializer(typeof(T)); using (StringReader sr3 = new StringReader(xml)) { XmlReaderSettings settings = new XmlReaderSettings() { CheckCharacters = false // default value is true; }; using (XmlReader xr3 = XmlTextReader.Create(sr3, settings)) { result = (T)serializer.Deserialize(xr3); } } return result; } 

Ele pode ter sido abordado em outro lugar, mas simplesmente alterar a linha de codificação da origem XML para ‘utf-16’ permite que o XML seja inserido em um tipo xml’data do SQL Server.

 using (DataSetTableAdapters.SQSTableAdapter tbl_SQS = new DataSetTableAdapters.SQSTableAdapter()) { try { bodyXML = @"< ?xml version="1.0" encoding="UTF-8" standalone="yes"?>"; bodyXMLutf16 = bodyXML.Replace("UTF-8", "UTF-16"); tbl_SQS.Insert(messageID, receiptHandle, md5OfBody, bodyXMLutf16, sourceType); } catch (System.Data.SqlClient.SqlException ex) { Console.WriteLine(ex.Message); Console.ReadLine(); } } 

O resultado é que todo o texto XML é inserido no campo de tipo de dados ‘xml’, mas a linha ‘header’ é removida. O que você vê no registro resultante é apenas

  

Usar o método de serialização descrito na input “Respondida” é uma maneira de include o header original no campo de destino, mas o resultado é que o texto XML restante é colocado em uma marca XML .

O adaptador de tabela no código é uma class criada automaticamente usando o assistente “Adicionar nova fonte de dados: Visual Studio 2013”. Os cinco parâmetros para o método Insert são mapeados para campos em uma tabela do SQL Server.