<TL; DR> Masalahnya cukup sederhana, sebenarnya: Anda tidak mencocokkan pengkodean yang dinyatakan (dalam deklarasi XML) dengan tipe data dari parameter input. Jika Anda menambahkan <?xml version="1.0" encoding="utf-8"?><test/>
ke string secara manual , maka mendeklarasikan SqlParameter
menjadi tipe SqlDbType.Xml
atau SqlDbType.NVarChar
akan memberi Anda kesalahan "tidak dapat mengalihkan pengkodean". Kemudian, saat memasukkan secara manual melalui T-SQL, karena Anda mengganti encoding yang dideklarasikan menjadi utf-16
, Anda dengan jelas memasukkan VARCHAR
string (tidak diawali dengan huruf besar "N", karenanya encoding 8-bit, seperti UTF-8) dan bukan sebuah NVARCHAR
string (diawali dengan huruf besar "N", karenanya pengkodean UTF-16 LE 16-bit).
Perbaikannya seharusnya sesederhana:
- Dalam kasus pertama, saat menambahkan deklarasi yang menyatakan
encoding="utf-8"
: jangan tambahkan deklarasi XML.
- Dalam kasus kedua, saat menambahkan deklarasi yang menyatakan
encoding="utf-16"
: baik
- cukup jangan menambahkan deklarasi XML, ATAU
- cukup tambahkan "N" ke jenis parameter masukan:
SqlDbType.NVarChar
alih-alih SqlDbType.VarChar
:-) (atau bahkan mungkin beralih menggunakan SqlDbType.Xml
)
(Tanggapan rinci ada di bawah)
Semua jawaban di sini terlalu rumit dan tidak perlu (terlepas dari 121 dan 184 suara untuk jawaban Christian dan Jon, masing-masing). Mereka mungkin memberikan kode yang berfungsi, tetapi tidak satupun dari mereka benar-benar menjawab pertanyaan tersebut. Masalahnya adalah tidak ada yang benar-benar memahami pertanyaan tersebut, yang pada akhirnya adalah tentang cara kerja tipe data XML di SQL Server. Tidak ada yang menentang kedua orang yang jelas cerdas itu, tetapi pertanyaan ini tidak ada hubungannya dengan serialisasi ke XML. Menyimpan data XML ke SQL Server jauh lebih mudah daripada yang tersirat di sini.
Tidak masalah bagaimana XML diproduksi selama Anda mengikuti aturan cara membuat data XML di SQL Server. Saya memiliki penjelasan yang lebih menyeluruh (termasuk kode contoh yang berfungsi untuk mengilustrasikan poin yang diuraikan di bawah) dalam jawaban atas pertanyaan ini: Bagaimana mengatasi kesalahan "tidak dapat mengganti pengkodean" saat memasukkan XML ke SQL Server , tetapi dasarnya adalah:
- Deklarasi XML adalah opsional
- Jenis data XML selalu menyimpan string sebagai UCS-2 / UTF-16 LE
- Jika XML Anda UCS-2 / UTF-16 LE, maka Anda:
- meneruskan data sebagai
NVARCHAR(MAX)
atau XML
/ SqlDbType.NVarChar
(maxsize = -1) atau SqlDbType.Xml
, atau jika menggunakan literal string maka harus diawali dengan huruf besar "N".
- jika menentukan deklarasi XML, harus "UCS-2" atau "UTF-16" (tidak ada perbedaan nyata di sini)
- Jika XML Anda dienkode 8-bit (mis. "UTF-8" / "iso-8859-1" / "Windows-1252"), maka Anda:
- perlu menentukan deklarasi XML JIKA encoding berbeda dari halaman kode yang ditentukan oleh Collation default database
- Anda harus memasukkan data sebagai
VARCHAR(MAX)
/ SqlDbType.VarChar
(maxsize = -1), atau jika menggunakan string literal maka tidak boleh diawali dengan huruf besar "N".
- Apa pun pengkodean 8-bit yang digunakan, "pengkodean" yang dicatat dalam deklarasi XML harus cocok dengan pengkodean byte sebenarnya.
- Pengkodean 8-bit akan diubah menjadi UTF-16 LE dengan tipe data XML
Dengan memperhatikan poin-poin yang diuraikan di atas, dan mengingat bahwa string dalam .NET selalu UTF-16 LE / UCS-2 LE (tidak ada perbedaan di antara keduanya dalam hal encoding), kami dapat menjawab pertanyaan Anda:
Apakah ada alasan mengapa saya tidak boleh menggunakan StringWriter untuk membuat serial Objek ketika saya membutuhkannya sebagai string sesudahnya?
Tidak, StringWriter
kode Anda tampaknya baik-baik saja (setidaknya saya tidak melihat masalah dalam pengujian terbatas saya menggunakan blok kode ke-2 dari pertanyaan).
Tidakkah pengaturan encoding ke UTF-16 (dalam tag xml) akan berfungsi?
Tidak perlu memberikan deklarasi XML. Jika tidak ada, pengkodean dianggap UTF-16 LE jika Anda meneruskan string ke SQL Server sebagai NVARCHAR
(yaitu SqlDbType.NVarChar
) atau XML
(yaitu SqlDbType.Xml
). Pengkodean diasumsikan sebagai Halaman Kode 8-bit default jika dikirimkan sebagai VARCHAR
(yaitu SqlDbType.VarChar
). Jika Anda memiliki karakter non-standar-ASCII (yaitu nilai 128 ke atas) dan mengirimkan sebagai VARCHAR
, maka Anda mungkin akan melihat "?" untuk karakter BMP dan "??" untuk Karakter Tambahan karena SQL Server akan mengubah string UTF-16 dari .NET menjadi string 8-bit dari Halaman Kode Database saat ini sebelum mengubahnya kembali menjadi UTF-16 / UCS-2. Tetapi Anda seharusnya tidak mendapatkan kesalahan apa pun.
Di sisi lain, jika Anda menentukan deklarasi XML, maka Anda harus meneruskan ke SQL Server menggunakan tipe data 8-bit atau 16-bit yang cocok. Jadi, jika Anda memiliki deklarasi yang menyatakan bahwa encodingnya adalah UCS-2 atau UTF-16, Anda harus meneruskan sebagai SqlDbType.NVarChar
atau SqlDbType.Xml
. Atau, jika Anda memiliki sebuah deklarasi yang menyatakan bahwa pengkodean adalah salah satu pilihan 8-bit (yaitu UTF-8
, Windows-1252
, iso-8859-1
, dll), maka Anda harus lulus dalam sebagai SqlDbType.VarChar
. Kegagalan untuk mencocokkan pengkodean yang dinyatakan dengan tipe data SQL Server 8 atau 16-bit yang tepat akan mengakibatkan kesalahan "tidak dapat mengalihkan pengkodean" yang Anda dapatkan.
Misalnya, menggunakan StringWriter
kode serialisasi berbasis Anda , saya hanya mencetak string yang dihasilkan dari XML dan menggunakannya di SSMS. Seperti yang Anda lihat di bawah, deklarasi XML disertakan (karena StringWriter
tidak memiliki opsi untuk OmitXmlDeclaration
suka XmlWriter
), yang tidak menimbulkan masalah selama Anda meneruskan string sebagai tipe data SQL Server yang benar:
-- 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>
Seperti yang Anda lihat, ia bahkan menangani karakter di luar ASCII standar, mengingat itu ሴ
adalah BMP Code Point U + 1234, dan 😸
Supplementary Character Code Point U + 1F638. Namun, berikut ini:
-- No upper-case "N" prefix on the string literal, hence VARCHAR:
DECLARE @Xml XML = '<?xml version="1.0" encoding="utf-16"?>
<string>Test ሴ😸</string>';
menghasilkan kesalahan berikut:
Msg 9402, Level 16, State 1, Line XXXXX
XML parsing: line 1, character 39, unable to switch the encoding
Ergo, selain semua penjelasan itu, solusi lengkap untuk pertanyaan awal Anda adalah:
Anda dengan jelas memasukkan string sebagai SqlDbType.VarChar
. Beralih ke SqlDbType.NVarChar
dan ini akan berfungsi tanpa perlu melalui langkah tambahan untuk menghapus deklarasi XML. Ini lebih disukai daripada menyimpan SqlDbType.VarChar
dan menghapus deklarasi XML karena solusi ini akan mencegah kehilangan data ketika XML menyertakan karakter non-standar-ASCII. Sebagai contoh:
-- 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>
Seperti yang Anda lihat, tidak ada kesalahan kali ini, tetapi sekarang ada kehilangan data 🙀.