2009-10-14 16 views
81

Actualmente estoy buscando una manera fácil de serializar objetos (en C# 3).Uso de StringWriter para la serialización XML

Busqué en Google algunos ejemplos y se acercó con 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()); 

Después de leer este question me pregunté, ¿por qué no usar StringWriter? Parece mucho más fácil.

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

Otro problema era que el primer ejemplo genera XML que no podía simplemente escribir en una columna XML de SQL Server 2005 DB.

La primera pregunta es: ¿Hay alguna razón por la que no deba usar StringWriter para serializar un Objeto cuando lo necesito como una cadena después? Nunca encontré un resultado usando StringWriter cuando busco en Google.

La segunda es, por supuesto: si no debería hacerlo con StringWriter (por cualquier motivo), ¿cuál sería una buena y correcta forma?


Adición:

Como ya se mencionó por ambas respuestas, voy a ir más lejos en el XML a DB problema.

Al escribir en la base de datos que tiene la siguiente excepción:

System.Data.SqlClient.SqlException: de tratamiento de XML: línea 1, 38 caracteres, incapaz de cambiar la codificación

Para

cadena

<?xml version="1.0" encoding="utf-8"?><test/> 

Tomé la cadena creada desde el XmlTextWriter y sólo hay que poner como º xml antes de. Éste no funcionó (ni con la inserción manual en el DB).

Después intenté la inserción manual (simplemente escribiendo INSERT INTO ...) con encoding = "utf-16" que también falló. Eliminar la codificación totalmente trabajada en ese momento. Después de ese resultado cambié de nuevo al código de StringWriter y voilé, funcionó.

Problema: Realmente no entiendo por qué.

en Christian Hayter: Con esas pruebas no estoy seguro de tener que usar utf-16 para escribir en la base de datos. ¿No funcionaría entonces la configuración de la codificación a UTF-16 (en la etiqueta xml)?

+1

estoy pasando por la experiencia personal. SQL Server solo acepta UTF-16, y si lo pasa de otra manera, estará a merced del analizador XML de SQL Server y sus intentos de convertir los datos. En lugar de intentar encontrar una forma de engañarlo , Simplemente lo paso UTF-16 directamente, que siempre funcionará. –

+0

¿Cómo está escribiendo esto en la base de datos? ¿Le está pasando una cadena, o una matriz de bytes, o escribiendo en una secuencia? Si es cualquiera de estos últimos dos formas, debe asegurarse de que su codificación declarada coincida con la codificación real de sus datos binarios. –

+0

phew. El manual intenté hacer como Query en el MS SQL Management Studio. Los intentos "codificados" se escribieron en una cadena que luego se pasó a un O/R Mapper que escribe como una cadena (hasta donde pude seguir). De hecho, le paso la cadena que se creó en los dos ejemplos que figuran en mi pregunta. – StampedeXV

Respuesta

121

Al serializar un documento XML en una cadena .NET, la codificación debe establecerse en UTF-16. Las cadenas se almacenan como UTF-16 internamente, por lo que esta es la única codificación que tiene sentido. Si desea almacenar datos en una codificación diferente, en su lugar utiliza una matriz de bytes.

SQL Server funciona según un principio similar; cualquier cadena que pase a una columna xml debe codificarse como UTF-16. SQL Server rechazará cualquier cadena donde la declaración XML no especifique UTF-16. Si la declaración XML no está presente, el estándar XML requiere que sea por defecto UTF-8, por lo que SQL Server también lo rechazará.

Teniendo esto en cuenta, aquí hay algunos métodos de utilidad para realizar la conversión.

public static string Serialize<T>(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<T>(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); 
     } 
    } 
} 
+0

Ver la adición de la pregunta. No entiendo los resultados de la prueba, parece _ contradecir su afirmación de que la BD siempre quiere/toma/necesita UTF-16. – StampedeXV

+6

* No * tiene que codificar como UTF-16, pero debe asegurarse de que la codificación que utiliza coincida con lo que espera el 'StringWriter'. Ver mi respuesta El formato de almacenamiento interno es irrelevante aquí. –

+0

bien que entiendo. En mi nuevo ejemplo: al dejar completamente fuera de la codificación, el DB decidió por sí mismo qué codificación se utilizó, por eso funcionó. ¿Lo entiendo correcto ahora? – StampedeXV

167

Un problema con StringWriter es que por defecto it doesn't let you set the encoding which it advertises - por lo que puede terminar con un documento XML anunciando su codificación como UTF-16, lo que significa que necesita codificarlo como UTF-16 si lo escribe en un archivo.Tengo una pequeña clase para ayudar con eso sin embargo:

public sealed class StringWriterWithEncoding : StringWriter 
{ 
    public override Encoding Encoding { get; } 

    public StringWriterWithEncoding (Encoding encoding) 
    { 
     Encoding = encoding; 
    }  
} 

O si sólo necesita UTF-8 (que es todo lo que necesitan a menudo):

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

En cuanto a por qué no se podría ahorrar su XML a la base de datos: tendrá que darnos más detalles sobre lo que sucedió cuando lo intentó, si desea que podamos diagnosticarlo o corregirlo.

+0

He entrado en más detalles para el problema de la base de datos ahora. Ver pregunta. – StampedeXV

+2

Triste el 'StringWriter' no tiene en cuenta la codificación, pero no obstante, gracias por un pequeño e ingenioso método :) – Chau

+2

Y" Análisis XML: línea 1, carácter 38, no se puede cambiar la codificación "puede ser resuelto por "settings.Indent = false; settings.OmitXmlDeclaration = false; " –

18

Antes que nada, tenga cuidado de encontrar ejemplos antiguos. Has encontrado uno que usa XmlTextWriter, que está en desuso desde .NET 2.0. XmlWriter.Create se debe utilizar en su lugar.

Aquí está un ejemplo de la serialización de un objeto en una columna 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(); 
       } 
      } 
     } 
    } 
} 
+2

Solo puedo votar esto una vez, pero esto merece ser la mejor respuesta aquí. Al final, no importa qué codificación se declare o utilice, siempre que 'XmlReader' pueda analizarlo. Se enviará previamente analizado a la base de datos, y luego el DB no necesita saber nada acerca de las codificaciones de caracteres - UTF-16 o de otro modo. En particular, tenga en cuenta que las declaraciones XML ni siquiera se conservan con los datos en la base de datos, independientemente del método que se utilice para insertarlo. Por favor, no desperdicie ejecutando XML a través de conversiones adicionales, como se muestra en otras respuestas aquí y en otros lugares. – ziesemer

+1

Ver también: http://stackoverflow.com/a/8998183/751158 – ziesemer

1
public static T DeserializeFromXml<T>(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; 
} 
0

Puede haber sido cubierto en otro lugar pero simplemente cambiando la codificación de línea de la fuente de XML a 'utf-16' permite que el XML para ser insertado en un tipo de datos 'xml' de SQL Server.

using (DataSetTableAdapters.SQSTableAdapter tbl_SQS = new DataSetTableAdapters.SQSTableAdapter()) 
{ 
    try 
    { 
     bodyXML = @"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><test></test>"; 
     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(); 
    } 
} 

El resultado es todo el texto XML se inserta en el campo de tipo de datos 'XML' pero se elimina la línea 'cabecera'. Lo que se ve en el registro resultante es sólo

<test></test> 

Utilizando el método de serialización se describe en la entrada "respondió" es una forma de incluir la cabecera original en el campo de destino, pero el resultado es que el texto XML restante está encerrado en una etiqueta XML <string></string>.

El adaptador de la tabla en el código es una clase incorporada automáticamente utilizando el Visual Studio 2013 "Agregar nuevo origen de datos:.. Asistente Los cinco parámetros al método Insert mapa de campos en una tabla de SQL Server

+1

Reemplazar? Esto es muy gracioso. –

+1

En serio, no hagas esto. Nunca. ¿Qué pasa si quiero incluir algo de prosa en mi xml que menciona "UTF-8"? ¡Acabas de cambiar mis datos a algo que no dije! –

Cuestiones relacionadas