<TL; DR> Das Problem ist eigentlich recht einfach: Sie stimmen die deklarierte Codierung (in der XML-Deklaration) nicht mit dem Datentyp des Eingabeparameters ab. Wenn Sie <?xml version="1.0" encoding="utf-8"?><test/>
die Zeichenfolge manuell hinzugefügt haben und dann deklarieren SqlParameter
, dass sie vom Typ ist, SqlDbType.Xml
oder SqlDbType.NVarChar
wenn der Fehler "Codierung kann nicht geändert werden" angezeigt wird. Wenn Sie dann manuell über T-SQL einfügen, haben Sie, da Sie die deklarierte Codierung auf "Umgestellt" geändert haben utf-16
, eindeutig eine VARCHAR
Zeichenfolge eingefügt (ohne ein "N" in Großbuchstaben, daher eine 8-Bit-Codierung wie UTF-8). und nicht einNVARCHAR
Zeichenfolge (mit einem vorangestellten "N" in Großbuchstaben, daher die 16-Bit-UTF-16-LE-Codierung).
Das Update sollte so einfach sein wie:
- Im ersten Fall beim Hinzufügen der Erklärung
encoding="utf-8"
: Fügen Sie einfach nicht die XML-Deklaration hinzu.
- Im zweiten Fall beim Hinzufügen der Erklärung
encoding="utf-16"
:
- Fügen Sie einfach nicht die XML-Deklaration ODER hinzu
- Fügen Sie einfach ein "N" zum Eingabeparametertyp hinzu:
SqlDbType.NVarChar
anstelle von SqlDbType.VarChar
:-) (oder wechseln Sie möglicherweise sogar zu "Verwenden" SqlDbType.Xml
)
(Detaillierte Antwort ist unten)
Alle Antworten hier sind zu kompliziert und unnötig (unabhängig von den 121 und 184 Up-Votes für Christians bzw. Jons Antworten). Sie stellen möglicherweise Arbeitscode bereit, aber keiner von ihnen beantwortet die Frage tatsächlich. Das Problem ist, dass niemand die Frage wirklich verstanden hat, die letztendlich die Funktionsweise des XML-Datentyps in SQL Server betrifft. Nichts gegen diese beiden eindeutig intelligenten Leute, aber diese Frage hat wenig bis gar nichts mit der Serialisierung in XML zu tun. Das Speichern von XML-Daten in SQL Server ist viel einfacher als hier impliziert.
Es spielt keine Rolle, wie das XML erstellt wird, solange Sie die Regeln zum Erstellen von XML-Daten in SQL Server befolgen. Ich habe eine ausführlichere Erklärung (einschließlich eines funktionierenden Beispielcodes zur Veranschaulichung der unten aufgeführten Punkte) in einer Antwort auf diese Frage: So lösen Sie den Fehler "Die Codierung kann nicht umgeschaltet werden" beim Einfügen von XML in SQL Server , aber die Grundlagen sind:
- Die XML-Deklaration ist optional
- Der XML-Datentyp speichert Zeichenfolgen immer als UCS-2 / UTF-16 LE
- Wenn Ihr XML UCS-2 / UTF-16 LE ist, dann haben Sie:
- Übergeben Sie die Daten entweder als
NVARCHAR(MAX)
oder XML
/ SqlDbType.NVarChar
(maxsize = -1) oderSqlDbType.Xml
, oder wenn Sie ein Zeichenfolgenliteral verwenden, muss ein Großbuchstabe "N" vorangestellt werden.
- Wenn Sie die XML-Deklaration angeben, muss diese entweder "UCS-2" oder "UTF-16" sein (hier kein wirklicher Unterschied).
- Wenn Ihr XML 8-Bit-codiert ist (z. B. "UTF-8" / "iso-8859-1" / "Windows-1252"), dann haben Sie:
- Die XML-Deklaration muss angegeben werden, wenn sich die Codierung von der Codepage unterscheidet, die in der Standardkollatierung der Datenbank angegeben ist
- Sie müssen die Daten als
VARCHAR(MAX)
/ SqlDbType.VarChar
(maxsize = -1) übergeben. Wenn Sie ein Zeichenfolgenliteral verwenden, darf diesem kein Großbuchstaben "N" vorangestellt werden.
- Unabhängig davon, welche 8-Bit-Codierung verwendet wird, muss die in der XML-Deklaration angegebene "Codierung" mit der tatsächlichen Codierung der Bytes übereinstimmen.
- Die 8-Bit-Codierung wird vom XML-Datentyp in UTF-16 LE konvertiert
Unter Berücksichtigung der oben genannten Punkte und angesichts der Tatsache, dass Zeichenfolgen in .NET immer UTF-16 LE / UCS-2 LE sind (es gibt keinen Unterschied zwischen diesen in Bezug auf die Codierung), können wir Ihre Fragen beantworten:
Gibt es einen Grund, warum ich StringWriter nicht zum Serialisieren eines Objekts verwenden sollte, wenn ich es anschließend als Zeichenfolge benötige?
Nein, Ihr StringWriter
Code scheint in Ordnung zu sein (zumindest sehe ich keine Probleme bei meinen eingeschränkten Tests mit dem 2. Codeblock aus der Frage).
Würde es dann nicht funktionieren, die Codierung auf UTF-16 (im XML-Tag) zu setzen?
Die XML-Deklaration muss nicht angegeben werden. Wenn es fehlt, wird angenommen, dass die Codierung UTF-16 LE ist, wenn Sie die Zeichenfolge als NVARCHAR
(dh SqlDbType.NVarChar
) oder XML
(dh SqlDbType.Xml
) an SQL Server übergeben . Es wird angenommen, dass die Codierung die Standard-8-Bit-Codepage ist, wenn sie als VARCHAR
(dh SqlDbType.VarChar
) übergeben wird. Wenn Sie Nicht-Standard-ASCII-Zeichen (dh Werte 128 und höher) haben und als übergebenVARCHAR
, wird wahrscheinlich "?" für BMP-Zeichen und "??" Für zusätzliche Zeichen konvertiert SQL Server die UTF-16-Zeichenfolge aus .NET in eine 8-Bit-Zeichenfolge der aktuellen Codepage der Datenbank, bevor sie wieder in UTF-16 / UCS-2 konvertiert wird. Aber Sie sollten keine Fehler bekommen.
Wenn Sie andererseits die XML-Deklaration angeben, müssen Sie mit dem passenden 8-Bit- oder 16-Bit-Datentyp an SQL Server übergeben. Wenn Sie also eine Erklärung haben, dass die Codierung entweder UCS-2 oder UTF-16 ist, müssen Sie als SqlDbType.NVarChar
oder übergeben SqlDbType.Xml
. Oder, wenn Sie eine Erklärung haben die besagt , dass die Codierung eine der 8-Bit - Optionen (dh UTF-8
, Windows-1252
, iso-8859-1
etc), dann Sie müssen in so passieren SqlDbType.VarChar
. Wenn die deklarierte Codierung nicht mit dem richtigen SQL Server-Datentyp mit 8 oder 16 Bit übereinstimmt, wird der Fehler "Codierung kann nicht geändert werden" angezeigt.
Zum Beispiel habe StringWriter
ich unter Verwendung Ihres basierten Serialisierungscodes einfach die resultierende Zeichenfolge des XML gedruckt und in SSMS verwendet. Wie Sie unten sehen können, ist die XML-Deklaration enthalten (da StringWriter
es keine Option zum OmitXmlDeclaration
Liken XmlWriter
gibt), was kein Problem darstellt, solange Sie die Zeichenfolge als korrekten SQL Server-Datentyp übergeben:
-- Upper-case "N" prefix == NVARCHAR, hence no error:
DECLARE @Xml XML = N'<?xml version="1.0" encoding="utf-16"?>
<string>Test ሴ😸</string>';
SELECT @Xml;
-- <string>Test ሴ😸</string>
Wie Sie sehen können, werden sogar Zeichen behandelt, die über das Standard-ASCII hinausgehen, vorausgesetzt, es ሴ
handelt sich um den BMP- 😸
Codepunkt U + 1234 und den zusätzlichen Zeichencodepunkt U + 1F638. Allerdings Folgendes:
-- No upper-case "N" prefix on the string literal, hence VARCHAR:
DECLARE @Xml XML = '<?xml version="1.0" encoding="utf-16"?>
<string>Test ሴ😸</string>';
führt zu folgendem Fehler:
Msg 9402, Level 16, State 1, Line XXXXX
XML parsing: line 1, character 39, unable to switch the encoding
Abgesehen von all diesen Erklärungen lautet die vollständige Lösung Ihrer ursprünglichen Frage:
Sie haben die Zeichenfolge eindeutig als übergeben SqlDbType.VarChar
. Wechseln Sie zu SqlDbType.NVarChar
und es funktioniert, ohne dass Sie den zusätzlichen Schritt zum Entfernen der XML-Deklaration ausführen müssen. Dies wird dem Beibehalten SqlDbType.VarChar
und Entfernen der XML-Deklaration vorgezogen, da diese Lösung Datenverlust verhindert, wenn das XML Nicht-Standard-ASCII-Zeichen enthält. Beispielsweise:
-- No upper-case "N" prefix on the string literal == VARCHAR, and no XML declaration:
DECLARE @Xml2 XML = '<string>Test ሴ😸</string>';
SELECT @Xml2;
-- <string>Test ???</string>
Wie Sie sehen können, gibt es diesmal keinen Fehler, aber jetzt gibt es Datenverlust 🙀.