c# utf8 Cómo resolver el error "no se puede cambiar la codificación" al insertar XML en SQL Server




xmlwriter encoding utf 8 c# (6)

Estoy tratando de insertar en la columna XML (SQL Server 2008 R2), pero el servidor se queja:

System.Data.SqlClient.SqlException (0x80131904):
Análisis XML: línea 1, carácter 39, no se puede cambiar la codificación

Descubrí que la columna XML debe ser UTF-16 para que la inserción sea exitosa.

El código que estoy usando es:

 XmlSerializer serializer = new XmlSerializer(typeof(MyMessage));
 StringWriter str = new StringWriter();
 serializer.Serialize(str, message);
 string messageToLog = str.ToString();

¿Cómo puedo serializar el objeto para que esté en la cadena UTF-8?

EDITAR : Ok, lo siento por la confusión - la cadena debe estar en UTF-8. Tenías razón: es UTF-16 por defecto, y si intento insertarlo en UTF-8, pasa. Entonces la pregunta es cómo serializar en UTF-8.

Ejemplo

Esto causa errores al intentar insertar en SQL Server:

    <?xml version="1.0" encoding="utf-16"?>
    <MyMessage>Teno</MyMessage>

Esto no:

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

Actualizar

Me di cuenta cuando el SQL Server 2008 para su tipo de columna Xml necesita utf-8, y cuando utf-16 en la propiedad de encoding de la especificación xml que está tratando de insertar:

Cuando desee agregar utf-8 , agregue parámetros al comando SQL de esta manera:

 sqlcmd.Parameters.Add("ParamName", SqlDbType.VarChar).Value = xmlValueToAdd;

Si intenta agregar xmlValueToAdd con encoding=utf-16 en la fila anterior, produciría errores en insert. Además, VarChar significa que los caracteres nacionales no son reconocidos (se convierten en signos de interrogación).

Para agregar utf-16 a db, utilice SqlDbType.NVarChar o SqlDbType.Xml en el ejemplo anterior, o simplemente no especifique el tipo:

 sqlcmd.Parameters.Add(new SqlParameter("ParamName", xmlValueToAdd));

La codificación predeterminada para un serializador xml debe ser UTF-16. Solo para asegurarte de que puedes intentar ...

XmlSerializer serializer = new XmlSerializer(typeof(YourObject));

// create a MemoryStream here, we are just working
// exclusively in memory
System.IO.Stream stream = new System.IO.MemoryStream();

// The XmlTextWriter takes a stream and encoding
// as one of its constructors
System.Xml.XmlTextWriter xtWriter = new System.Xml.XmlTextWriter(stream, Encoding.UTF16);

serializer.Serialize(xtWriter, yourObjectInstance);

xtWriter.Flush();

Me tomó una eternidad volver a resolver este problema.

Estaba haciendo una INSERT en SQL Server como algo así como:

UPDATE Customers 
SET data = '<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';

y esto da el error:

Msg 9402, nivel 16, estado 1, línea 2
Análisis XML: línea 1, carácter 39, no se puede cambiar la codificación

Y la solución realmente, muy simple es:

UPDATE Customers 
SET data = N'<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';

La diferencia es prefijando la cadena Unicode con N :

N '<? Xml version = "1.0" encoding = "utf-16"?> Teno </ MyMessage>'

En el primer caso, se supone que una cadena no prefijada es varchar (por ejemplo, la página de códigos de Windows-1252). Cuando encuentra la encoding="utf-16" dentro de la cadena, hay un conflicto (y con razón, ya que la cadena no es utf-16).

La solución es pasar la cadena al servidor SQL como un nvarchar (es decir, UTF-16):

N '<? Xml version = "1.0" encoding = "utf-16"?>'

De esa forma, la cadena es UTF-16, que coincide con la codificación utf-16 que el XML dice que es. La alfombra coincide con las cortinas, por así decirlo.


Una cadena siempre es UTF-16 en .NET, por lo que mientras permanezca dentro de su aplicación administrada no tiene que preocuparse de qué codificación es.

El problema es más probable cuando habla con el servidor SQL. Su pregunta no muestra ese código, por lo que es difícil precisar el error exacto. Mi sugerencia es que compruebe si hay una propiedad o atributo que puede establecer en ese código que especifica la codificación de los datos enviados al servidor.


Esta pregunta es casi duplicada de otras dos, y sorprendentemente, aunque esta es la más reciente, creo que falta la mejor respuesta.

Los duplicados, y lo que creo que son sus mejores respuestas, son:

Al final, no importa qué codificación se declare o utilice, siempre que XmlReader pueda analizarlo localmente dentro del servidor de la aplicación.

Como se confirmó en la forma más eficiente para leer XML en ADO.net desde la columna de tipo XML en el servidor SQL? , SQL Server almacena XML en un formato binario eficiente. Al usar la clase SqlXml , ADO.net puede comunicarse con SQL Server en este formato binario, y no requiere que el servidor de base de datos realice ninguna serialización o deserialización de XML. Esto también debería ser más eficiente para el transporte a través de la red.

Al usar SqlXml , XML 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 u otras. 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 refiérase a las respuestas arriba relacionadas para métodos que se ven muy similares a esto, pero este ejemplo es mío:

using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Xml;

static class XmlDemo {
    static void Main(string[] args) {
        using(SqlConnection conn = new SqlConnection()) {
            conn.ConnectionString = "...";
            conn.Open();

            using(SqlCommand cmd = new SqlCommand("Insert Into TestData(Xml) Values (@Xml)", conn)) {

                cmd.Parameters.Add(new SqlParameter("@Xml", SqlDbType.Xml) {
                    // Works.
                    // Value = "<Test/>"

                    // Works.  XML Declaration is not persisted!
                    // Value = "<?xml version=\"1.0\"?><Test/>"

                    // Works.  XML Declaration is not persisted!
                    // Value = "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Test/>"

                    // Error ("unable to switch the encoding" SqlException).
                    // Value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>"

                    // Works.  XML Declaration is not persisted!
                    Value = new SqlXml(XmlReader.Create(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>")))
                });

                cmd.ExecuteNonQuery();
            }
        }
    }
}

Tenga en cuenta que no consideraría el último ejemplo (no comentado) como "listo para producción", pero lo dejé tal como está para ser conciso y legible. Si se hace correctamente, tanto el StringReader como el XmlReader creado deben inicializarse dentro de sentencias using para garantizar que se XmlReader sus métodos Close() cuando se completen.

Por lo que he visto, las declaraciones XML nunca se conservan cuando se utiliza una columna XML. Incluso sin usar .NET y simplemente utilizando esta instrucción de inserción SQL directa, por ejemplo, la declaración XML no se guarda en la base de datos con el XML:

Insert Into TestData(Xml) Values ('<?xml version="1.0" encoding="UTF-8"?><Test/>');

Ahora, en términos de la pregunta del OP, el objeto a ser serializado todavía necesita ser convertido en una estructura XML del objeto MyMessage , y XmlSerializer todavía es necesario para esto. Sin embargo, en el peor de los casos, en lugar de serializar en una Cadena, el mensaje podría serializarse en un XmlDocument , que luego se puede pasar a SqlXml través de un nuevo XmlNodeReader , evitando un viaje de serialización / serialización a una cadena. (Consulte http://blogs.msdn.com/b/jongallant/archive/2007/01/30/how-to-convert-xmldocument-to-xmlreader-for-sqlxml-data-type.aspx para obtener detalles y un ejemplo) .)

Todo aquí fue desarrollado y probado con .NET 4.0 y SQL Server 2008 R2.

No desperdicie ejecutando XML a través de conversiones adicionales (deserializaciones y serializaciones, a DOM, cadenas u otros), como se muestra en otras respuestas aquí y en otros lugares.


Aunque una cadena de .NET siempre es UTF-16 , necesita serializar el objeto utilizando la UTF-16 . Eso debería ser algo como esto:

public static string ToString(object source, Type type, Encoding encoding)
{
    // The string to hold the object content
    String content;

    // Create a memoryStream into which the data can be written and readed
    using (var stream = new MemoryStream())
    {
        // Create the xml serializer, the serializer needs to know the type
        // of the object that will be serialized
        var xmlSerializer = new XmlSerializer(type);

        // Create a XmlTextWriter to write the xml object source, we are going
        // to define the encoding in the constructor
        using (var writer = new XmlTextWriter(stream, encoding))
        {
            // Save the state of the object into the stream
            xmlSerializer.Serialize(writer, source);

            // Flush the stream
            writer.Flush();

            // Read the stream into a string
            using (var reader = new StreamReader(stream, encoding))
            {
                // Set the stream position to the begin
                stream.Position = 0;

                // Read the stream into a string
                content = reader.ReadToEnd();
            }
        }
    }

    // Return the xml string with the object content
    return content;
}

Al establecer la codificación en Encoding.Unicode, no solo la cadena será UTF-16 sino que también debería obtener la cadena xml como UTF-16 .

<?xml version="1.0" encoding="utf-16"?>

¿No es la solución más fácil decirle al serializador que no muestre la declaración XML? .NET y SQL deberían ordenar el resto entre ellos.

        XmlSerializer serializer = new XmlSerializer(typeof(MyMessage));
        StringWriter str = new StringWriter();
        using (XmlWriter writer = XmlWriter.Create(str, new XmlWriterSettings { OmitXmlDeclaration = true }))
        {
            serializer.Serialize(writer, message);
        }
        string messageToLog = str.ToString();




utf-16