Actualmente estoy buscando una forma 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 SQL Server 2005 DB.
La primera pregunta es: ¿Hay alguna razón por la que no debería usar StringWriter para serializar un Objeto cuando lo necesito como cadena después? Nunca encontré un resultado usando StringWriter al buscar en Google.
La segunda es, por supuesto: si no debería hacerlo con StringWriter (por las razones que sean), ¿cuál sería una forma buena y correcta?
Adición:
Como ya se mencionó en ambas respuestas, profundizaré en el problema de XML a DB.
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 simplemente la puse como xml allí. Éste no funcionó (ni con inserción manual en la base de datos).
Luego probé la inserción manual (solo escribiendo INSERT INTO ...) con encoding = "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)?
fuente
Respuestas:
<TL; DR> El problema es bastante simple, en realidad: no está haciendo coincidir la codificación declarada (en la declaración XML) con el tipo de datos del parámetro de entrada. Si agregó manualmente
<?xml version="1.0" encoding="utf-8"?><test/>
a la cadena, entonces declararSqlParameter
que es de tipoSqlDbType.Xml
oSqlDbType.NVarChar
le daría el error "No se puede cambiar la codificación". Luego, al insertar manualmente a través de T-SQL, ya que cambió la codificación declarada para serutf-16
, claramente estaba insertando unaVARCHAR
cadena (no con el prefijo "N" en mayúscula, por lo tanto, una codificación de 8 bits, como UTF-8) y no unaNVARCHAR
cadena (con el prefijo "N" en mayúscula, de ahí la codificación UTF-16 LE de 16 bits).La solución debería haber sido tan simple como:
encoding="utf-8"
: simplemente no agregue la declaración XML.encoding="utf-16"
: o bienSqlDbType.NVarChar
lugar deSqlDbType.VarChar
:-) (o posiblemente incluso cambie a usarSqlDbType.Xml
)(La respuesta detallada está a continuación)
Todas las respuestas aquí son demasiado complicadas e innecesarias (independientemente de los 121 y 184 votos a favor de las respuestas de Christian y Jon, respectivamente). Es posible que proporcionen un código de trabajo, pero ninguno de ellos realmente responde a la pregunta. El problema es que nadie entendió realmente la pregunta, que en última instancia se trata de cómo funciona el tipo de datos XML en SQL Server. Nada en contra de esas dos personas claramente inteligentes, pero esta pregunta tiene poco o nada que ver con la serialización en XML. Guardar datos XML en SQL Server es mucho más fácil de lo que se implica aquí.
Realmente no importa cómo se produce el XML siempre que siga las reglas de cómo crear datos XML en SQL Server. Tengo una explicación más completa (incluido un código de ejemplo de trabajo para ilustrar los puntos que se describen a continuación) en una respuesta a esta pregunta: Cómo resolver el error "no se puede cambiar la codificación" al insertar XML en SQL Server , pero los conceptos básicos son:
NVARCHAR(MAX)
oXML
/SqlDbType.NVarChar
(maxsize = -1) oSqlDbType.Xml
, o si usa una cadena literal, debe tener el prefijo "N" en mayúscula.VARCHAR(MAX)
/SqlDbType.VarChar
(maxsize = -1), o si se utiliza una cadena literal entonces debe no ser prefijado con una mayúscula "N".Teniendo en cuenta los puntos descritos anteriormente, y dado que las cadenas en .NET siempre son UTF-16 LE / UCS-2 LE (no hay diferencia entre ellas en términos de codificación), podemos responder a sus preguntas:
No, su
StringWriter
código parece estar bien (al menos no veo problemas en mis pruebas limitadas usando el segundo bloque de código de la pregunta).No es necesario proporcionar la declaración XML. Cuando falta, se asume que la codificación es UTF-16 LE si pasa la cadena a SQL Server como
NVARCHAR
(ieSqlDbType.NVarChar
) oXML
(ieSqlDbType.Xml
). Se supone que la codificación es la página de códigos de 8 bits predeterminada si se pasa comoVARCHAR
(es decirSqlDbType.VarChar
). Si tiene caracteres ASCII no estándar (es decir, valores 128 y superiores) y los está pasando comoVARCHAR
, es probable que vea "?" para caracteres BMP y "??" para caracteres suplementarios, ya que SQL Server convertirá la cadena UTF-16 de .NET en una cadena de 8 bits de la página de códigos de la base de datos actual antes de volver a convertirla en UTF-16 / UCS-2. Pero no debería recibir ningún error.Por otro lado, si especifica la declaración XML, debe pasar a SQL Server utilizando el tipo de datos de 8 o 16 bits correspondiente. Entonces, si tiene una declaración que indica que la codificación es UCS-2 o UTF-16, debe pasar como
SqlDbType.NVarChar
oSqlDbType.Xml
. O, si usted tiene una declaración de que la codificación es una de las opciones de 8 bits (es decirUTF-8
,Windows-1252
,iso-8859-1
, etc), entonces usted debe pasar en tanSqlDbType.VarChar
. Si no coincide la codificación declarada con el tipo de datos de SQL Server de 8 o 16 bits adecuado, se producirá el error "No se puede cambiar la codificación" que estaba recibiendo.Por ejemplo, usando su
StringWriter
código de serialización basado en, simplemente imprimí la cadena resultante del XML y la usé en SSMS. Como puede ver a continuación, se incluye la declaración XML (porqueStringWriter
no tiene una opción para MeOmitXmlDeclaration
gustaXmlWriter
), lo que no plantea ningún problema siempre que pase la cadena como el tipo de datos SQL Server correcto:Como puede ver, incluso maneja caracteres más allá de ASCII estándar, dado que
ሴ
es el punto de código BMP U + 1234, y😸
es el punto de código de carácter suplementario U + 1F638. Sin embargo, lo siguiente:da como resultado el siguiente error:
Ergo, dejando de lado toda esa explicación, la solución completa a su pregunta original es:
Claramente estabas pasando la cuerda como
SqlDbType.VarChar
. Cambie aSqlDbType.NVarChar
y funcionará sin necesidad de pasar por el paso adicional de eliminar la declaración XML. Se prefiere a mantenerSqlDbType.VarChar
y eliminar la declaración XML porque esta solución evitará la pérdida de datos cuando el XML incluye caracteres ASCII no estándar. Por ejemplo:Como puede ver, esta vez no hay ningún error, pero ahora hay pérdida de datos 🙀.
fuente
SqlDbType.NVarChar
oXml
.Un problema
StringWriter
es 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:O si solo necesita UTF-8 (que es todo lo que a menudo necesito):
En cuanto a por qué no pudo guardar su XML en la base de datos, tendrá que darnos más detalles sobre lo que sucedió cuando lo intentó, si desea que podamos diagnosticarlo / solucionarlo.
fuente
StringWriter
, no tiene en cuenta la codificación, pero no obstante, gracias por un pequeño método ingenioso :)MemoryStream
aStreamWriter
con la codificación correcta.StreamWriter
es unTextWriter
(el tipo queXmlWriter.Create
espera) con codificación personalizable, después de todo.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, use una matriz de bytes en su lugar.
SQL Server funciona con un principio similar; cualquier cadena que se pase a una
xml
columna debe codificarse como UTF-16. SQL Server rechazará cualquier cadena en la que la declaración XML no especifique UTF-16. Si la declaración XML no está presente, entonces el estándar XML requiere que esté predeterminado en 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.
fuente
StringWriter
Espera. Mira mi respuesta. El formato de almacenamiento interno es irrelevante aquí.Nothing
implícitamente se puede convertir a cualquier tipo. He corregido elDeserialize
código. LaSerialize
advertencia debe ser solo para Resharper, el compilador por sí solo no se opone y es legal hacerlo.En primer lugar, tenga cuidado de no encontrar ejemplos antiguos. Ha encontrado uno que usa
XmlTextWriter
, que está obsoleto a partir de .NET 2.0.XmlWriter.Create
debería utilizarse en su lugar.Aquí hay un ejemplo de serialización de un objeto en una columna XML:
fuente
XmlReader
pueda analizar. Se enviará previamente analizado a la base de datos, y luego la base de datos no necesita saber nada sobre codificaciones de caracteres, UTF-16 o de otro tipo. En particular, tenga en cuenta que las declaraciones XML ni siquiera se conservan con los datos de la base de datos, independientemente del método que se utilice para insertarlos. No desperdicie ejecutando XML a través de conversiones adicionales, como se muestra en otras respuestas aquí y en otros lugares.fuente
Es posible que se haya cubierto en otro lugar, pero simplemente cambiar la línea de codificación de la fuente XML a 'utf-16' permite que el XML se inserte en un tipo de datos 'xml' de SQL Server.
El resultado es que todo el texto XML se inserta en el campo de tipo de datos 'xml', pero se elimina la línea de 'encabezado'. Lo que ves en el registro resultante es solo
El uso del método de serialización descrito en la entrada "Respondido" es una forma de incluir el encabezado original en el campo de destino, pero el resultado es que el texto XML restante se incluye en una
<string></string>
etiqueta XML .El adaptador de tabla en el código es una clase creada automáticamente con el asistente "Agregar nueva fuente de datos: de Visual Studio 2013". Los cinco parámetros del método Insertar se asignan a los campos de una tabla de SQL Server.
fuente