Je recherche actuellement un moyen simple de sérialiser des objets (en C # 3).
J'ai googlé quelques exemples et suis venu avec quelque chose comme:
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());
Après avoir lu cette question que je me suis posée, pourquoi ne pas utiliser StringWriter? Cela semble beaucoup plus facile.
XmlSerializer ser = new XmlSerializer(typeof(MyObject));
StringWriter writer = new StringWriter();
ser.Serialize(writer, myObject);
serializedValue = writer.ToString();
Un autre problème était que le premier exemple générait du XML que je ne pouvais pas simplement écrire dans une colonne XML de SQL Server 2005 DB.
La première question est: y a-t-il une raison pour laquelle je ne devrais pas utiliser StringWriter pour sérialiser un objet lorsque j'en ai besoin sous forme de chaîne par la suite? Je n'ai jamais trouvé de résultat en utilisant StringWriter lors de la recherche sur Google.
La seconde est, bien sûr: si vous ne devriez pas le faire avec StringWriter (pour quelque raison que ce soit), quelle serait une bonne et correcte manière?
Une addition:
Comme cela a déjà été mentionné par les deux réponses, je vais approfondir le problème XML to DB.
Lors de l'écriture dans la base de données, j'ai eu l'exception suivante:
System.Data.SqlClient.SqlException: analyse XML: ligne 1, caractère 38, impossible de changer le codage
Pour chaîne
<?xml version="1.0" encoding="utf-8"?><test/>
J'ai pris la chaîne créée à partir de XmlTextWriter et je l'ai juste mise au format xml. Celui-ci ne fonctionnait pas (ni avec insertion manuelle dans la base de données).
Ensuite, j'ai essayé l'insertion manuelle (juste en écrivant INSERT INTO ...) avec encoding = "utf-16" qui a également échoué. La suppression de l'encodage a alors totalement fonctionné. Après ce résultat, je suis revenu au code StringWriter et le tour est joué - cela a fonctionné.
Problème: je ne comprends pas vraiment pourquoi.
chez Christian Hayter: Avec ces tests, je ne suis pas sûr de devoir utiliser utf-16 pour écrire dans la base de données. La définition de l'encodage sur UTF-16 (dans la balise xml) ne fonctionnerait-elle pas alors?
la source
Réponses:
<TL; DR> Le problème est plutôt simple, en fait: vous ne faites pas correspondre le codage déclaré (dans la déclaration XML) avec le type de données du paramètre d'entrée. Si vous avez ajouté manuellement
<?xml version="1.0" encoding="utf-8"?><test/>
à la chaîne, alors déclarer leSqlParameter
comme étant de typeSqlDbType.Xml
ouSqlDbType.NVarChar
vous donnerait l'erreur «impossible de changer de codage». Ensuite, lors de l'insertion manuelle via T-SQL, puisque vous avez changé le codage déclaré pour êtreutf-16
, vous insérez clairement uneVARCHAR
chaîne (non préfixée par un «N» majuscule, d'où un codage 8 bits, tel que UTF-8) et non uneNVARCHAR
chaîne (préfixée par un "N" majuscule, d'où le codage UTF-16 LE 16 bits).Le correctif aurait dû être aussi simple que:
encoding="utf-8"
: n'ajoutez simplement pas la déclaration XML.encoding="utf-16"
: soitSqlDbType.NVarChar
au lieu deSqlDbType.VarChar
:-) (ou éventuellement passez à usingSqlDbType.Xml
)(La réponse détaillée est ci-dessous)
Toutes les réponses ici sont trop compliquées et inutiles (indépendamment des 121 et 184 votes positifs pour les réponses de Christian et Jon, respectivement). Ils peuvent fournir un code fonctionnel, mais aucun d'entre eux ne répond réellement à la question. Le problème est que personne n'a vraiment compris la question, qui porte finalement sur le fonctionnement du type de données XML dans SQL Server. Rien contre ces deux personnes clairement intelligentes, mais cette question n'a rien à voir avec la sérialisation vers XML. L'enregistrement des données XML dans SQL Server est beaucoup plus facile que ce qui est sous-entendu ici.
La façon dont le XML est produit n'a pas vraiment d'importance tant que vous suivez les règles de création de données XML dans SQL Server. J'ai une explication plus approfondie (y compris un exemple de code de travail pour illustrer les points décrits ci-dessous) dans une réponse à cette question: Comment résoudre l'erreur «impossible de changer de codage» lors de l'insertion de XML dans SQL Server , mais les bases sont:
NVARCHAR(MAX)
ouXML
/SqlDbType.NVarChar
(maxsize = -1) ouSqlDbType.Xml
, ou si vous utilisez une chaîne littérale, elle doit être précédée d'un "N" majuscule.VARCHAR(MAX)
/SqlDbType.VarChar
(maxsize = -1), ou si vous utilisez une chaîne littérale, elle ne doit pas être précédée d'un «N» majuscule.Avec les points décrits ci-dessus à l'esprit, et étant donné que les chaînes dans .NET sont toujours UTF-16 LE / UCS-2 LE (il n'y a aucune différence entre celles-ci en termes d'encodage), nous pouvons répondre à vos questions:
Non, votre
StringWriter
code semble très bien (au moins je ne vois aucun problème dans mes tests limités en utilisant le 2ème bloc de code de la question).Il n'est pas nécessaire de fournir la déclaration XML. Lorsqu'il est manquant, le codage est supposé être UTF-16 LE si vous passez la chaîne dans SQL Server en tant que
NVARCHAR
(ieSqlDbType.NVarChar
) ouXML
(ieSqlDbType.Xml
). Le codage est supposé être la page de codes 8 bits par défaut s'il est transmis en tant queVARCHAR
(c.-à-d.SqlDbType.VarChar
). Si vous avez des caractères ASCII non standard (c'est-à-dire des valeurs de 128 et plus) et queVARCHAR
vous passez en tant que , vous verrez probablement "?" pour les caractères BMP et "??" pour les caractères supplémentaires, car SQL Server convertira la chaîne UTF-16 de .NET en une chaîne 8 bits de la page de codes de la base de données actuelle avant de la reconvertir en UTF-16 / UCS-2. Mais vous ne devriez pas avoir d'erreur.En revanche, si vous spécifiez la déclaration XML, vous devez passer à SQL Server en utilisant le type de données 8 bits ou 16 bits correspondant. Donc, si vous avez une déclaration indiquant que le codage est soit UCS-2, soit UTF-16, vous devez passer en tant que
SqlDbType.NVarChar
ouSqlDbType.Xml
. Ou, si vous avez une déclaration indiquant que le codage est ( à savoir l' une des options 8 bitsUTF-8
,Windows-1252
,iso-8859-1
, etc.), alors vous devez passer commeSqlDbType.VarChar
. Le fait de ne pas faire correspondre le codage déclaré avec le type de données SQL Server 8 ou 16 bits approprié entraînera l'erreur «Impossible de changer le codage» que vous obteniez.Par exemple, en utilisant votre
StringWriter
code de sérialisation basé sur votre code, j'ai simplement imprimé la chaîne résultante du XML et l'ai utilisée dans SSMS. Comme vous pouvez le voir ci-dessous, la déclaration XML est incluse (car elleStringWriter
n'a pas d'option pourOmitXmlDeclaration
aimerXmlWriter
), ce qui ne pose aucun problème tant que vous passez la chaîne en tant que type de données SQL Server correct:Comme vous pouvez le voir, il gère même les caractères au-delà de l'ASCII standard, étant donné qu'il
ሴ
s'agit du point de code BMP U + 1234 et du😸
point de code de caractère supplémentaire U + 1F638. Cependant, ce qui suit:entraîne l'erreur suivante:
Ergo, toutes ces explications mises à part, la solution complète à votre question initiale est:
Vous passiez clairement la chaîne en tant que
SqlDbType.VarChar
. Basculez versSqlDbType.NVarChar
et cela fonctionnera sans avoir à passer par l'étape supplémentaire de suppression de la déclaration XML. Cela est préférable à la conservationSqlDbType.VarChar
et à la suppression de la déclaration XML car cette solution empêchera la perte de données lorsque le XML comprend des caractères ASCII non standard. Par exemple:Comme vous pouvez le voir, il n'y a pas d'erreur cette fois, mais maintenant il y a une perte de données 🙀.
la source
SqlDbType.NVarChar
ouXml
.Un problème avec
StringWriter
est que par défaut, il ne vous permet pas de définir l'encodage qu'il annonce - vous pouvez donc vous retrouver avec un document XML annonçant son encodage en UTF-16, ce qui signifie que vous devez l'encoder en UTF-16 si vous l'écrire dans un fichier. J'ai une petite classe pour aider avec ça:Ou si vous n'avez besoin que de l'UTF-8 (ce dont j'ai souvent besoin):
Quant à savoir pourquoi vous n'avez pas pu enregistrer votre XML dans la base de données, vous devrez nous donner plus de détails sur ce qui s'est passé lorsque vous avez essayé, si vous voulez que nous puissions le diagnostiquer / le réparer.
la source
StringWriter
ne prend pas en compte l'encodage, mais jamais moins, merci pour une petite méthode astucieuse :)MemoryStream
et aStreamWriter
avec le bon encodage.StreamWriter
est unTextWriter
(le type quiXmlWriter.Create
attend) avec un encodage personnalisable, après tout.Lors de la sérialisation d'un document XML dans une chaîne .NET, le codage doit être défini sur UTF-16. Les chaînes sont stockées au format UTF-16 en interne, c'est donc le seul encodage qui ait du sens. Si vous souhaitez stocker des données dans un codage différent, utilisez plutôt un tableau d'octets.
SQL Server fonctionne sur un principe similaire; toute chaîne passée dans un
xml
colonne doit être codée en UTF-16. SQL Server rejettera toute chaîne dans laquelle la déclaration XML ne spécifie pas UTF-16. Si la déclaration XML n'est pas présente, la norme XML exige qu'elle soit par défaut UTF-8, donc SQL Server la rejettera également.En gardant cela à l'esprit, voici quelques méthodes utilitaires pour effectuer la conversion.
la source
StringWriter
. Voyez ma réponse. Le format de stockage interne n'est pas pertinent ici.Nothing
est implicitement convertible en n'importe quel type. J'ai corrigé leDeserialize
code. L'Serialize
avertissement doit être une chose réservée à Resharper, le compilateur seul ne fait pas d'objection et il est légal de le faire.Tout d'abord, méfiez-vous de trouver d'anciens exemples. Vous en avez trouvé un qui utilise
XmlTextWriter
, qui est obsolète à partir de .NET 2.0.XmlWriter.Create
devrait être utilisé à la place.Voici un exemple de sérialisation d'un objet dans une colonne XML:
la source
XmlReader
peut l'analyser. Il sera envoyé pré-analysé à la base de données, puis la base de données n'a pas besoin de savoir quoi que ce soit sur les encodages de caractères - UTF-16 ou autre. En particulier, notez que les déclarations XML ne sont même pas persistantes avec les données de la base de données, quelle que soit la méthode utilisée pour l'insérer. Veuillez ne pas gaspiller en exécutant XML via des conversions supplémentaires, comme indiqué dans d'autres réponses ici et ailleurs.la source
Cela a peut-être été traité ailleurs, mais le simple fait de changer la ligne de codage de la source XML en «utf-16» permet au XML d'être inséré dans le type xml'data d'un SQL Server.
Le résultat est que tout le texte XML est inséré dans le champ de type de données «xml» mais que la ligne «en-tête» est supprimée. Ce que vous voyez dans l'enregistrement résultant est juste
L'utilisation de la méthode de sérialisation décrite dans l'entrée "Answered" est un moyen d'inclure l'en-tête d'origine dans le champ cible, mais le résultat est que le texte XML restant est inclus dans une
<string></string>
balise XML .L'adaptateur de table dans le code est une classe créée automatiquement à l'aide de l'assistant Ajouter une nouvelle source de données de Visual Studio 2013. Les cinq paramètres de la méthode Insertion sont mappés aux champs d'une table SQL Server.
la source