Usando StringWriter para la serialización XML

Resuelto StampedeXV asked hace 14 años • 7 respuestas

Actualmente estoy buscando una manera fácil de serializar objetos (en C# 3).

Busqué en Google algunos ejemplos y se me ocurrió 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 esta pregunta 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 fue que el primer ejemplo generó XML que no podía simplemente escribir en una columna XML de la base de datos SQL Server 2005.

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

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


Suma:

Como ya se mencionó en ambas respuestas, profundizaré en el problema de XML a base de datos.

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

System.Data.SqlClient.SqlException: análisis XML: línea 1, carácter 38, no se puede cambiar la codificación

Para cuerda

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

Tomé la cadena creada a partir de XmlTextWriter y la puse como xml allí. Éste no funcionó (tampoco con la inserción manual en la base de datos).

Luego intenté la inserción manual (simplemente escribiendo INSERT INTO...) con codificación="utf-16" que también falló. Eliminar la codificación funcionó totalmente entonces. Después de ese resultado, volví al código StringWriter y listo, 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. Entonces, ¿no funcionaría configurar la codificación en UTF-16 (en la etiqueta xml)?

StampedeXV avatar Oct 14 '09 14:10 StampedeXV
Aceptado

Un problema StringWriteres que, de forma predeterminada, no le permite configurar la codificación que anuncia , por lo que puede terminar con un documento XML que anuncia su codificación como UTF-16, lo que significa que debe codificarlo como UTF-16 si escríbalo en un archivo. Sin embargo, tengo una clase pequeña para ayudar con eso:

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

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

O si solo necesitas UTF-8 (que es todo lo que necesito a menudo):

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

En cuanto a por qué no pudo guardar su XML en la base de datos, deberá brindarnos más detalles sobre lo que sucedió cuando lo intentó, si desea que podamos diagnosticarlo o solucionarlo.

Jon Skeet avatar Oct 14 '2009 07:10 Jon Skeet

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

SQL Server funciona según un principio similar; cualquier cadena pasada a una xmlcolumna 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, entonces el estándar XML requiere que el valor predeterminado sea UTF-8, por lo que SQL Server también lo rechazará.

Teniendo esto en cuenta, a continuación se muestran algunos métodos útiles 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);
        }
    }
}
Christian Hayter avatar Oct 14 '2009 07:10 Christian Hayter