<TL; DR> Sorun aslında oldukça basittir: bildirilen kodlamayı (XML bildiriminde) input parametresinin veri türü ile eşleştirmiyorsunuz. Dizeye manuel olarak eklediyseniz , türünün <?xml version="1.0" encoding="utf-8"?><test/>
olduğunu bildirmek veya size "kodlama değiştirilemiyor" hatasını verir. Daha sonra, T-SQL aracılığıyla manuel olarak eklerken, bildirilen kodlamayı olarak değiştirdiğiniz için , açıkça bir dize ekliyordunuz (büyük harf "N" ile önek getirilmemiştir, dolayısıyla UTF-8 gibi 8 bit kodlama) ve bir dize değil (büyük harf "N" ile başlar, dolayısıyla 16 bit UTF-16 LE kodlaması).SqlParameter
SqlDbType.Xml
SqlDbType.NVarChar
utf-16
VARCHAR
NVARCHAR
Düzeltme şu kadar basit olmalıydı:
- İlk durumda, belirten bildirimi eklerken
encoding="utf-8"
: XML bildirimini eklemeyin.
- İkinci durumda, aşağıdaki ifadelerden
encoding="utf-16"
birini eklerken :
- XML bildirimini eklemeyin, VEYA
- giriş parametresi türüne basitçe bir "N" ekleyin: :-)
SqlDbType.NVarChar
yerine SqlDbType.VarChar
(veya hatta kullanmaya geçin SqlDbType.Xml
)
(Ayrıntılı yanıt aşağıdadır)
Buradaki tüm cevaplar aşırı karmaşık ve gereksizdir (sırasıyla Christian'ın ve Jon'un cevaplarının 121 ve 184'üne bakılmaksızın). Çalışma kodu sağlayabilirler, ancak hiçbiri aslında soruyu yanıtlamaz. Sorun şu ki, soruyu kimse gerçekten anlamadı, bu da sonuçta SQL Server'daki XML veri türünün nasıl çalıştığı ile ilgili. Bu iki açıkça zeki insana karşı bir şey yok, ancak bu sorunun XML'e serileştirmeyle pek ilgisi yok. XML verilerini SQL Server'a kaydetmek, burada ima edilenden çok daha kolaydır.
SQL Server'da XML verilerinin nasıl oluşturulacağına ilişkin kurallara uyduğunuz sürece XML'in nasıl üretildiği gerçekten önemli değildir. Bu sorunun cevabında daha kapsamlı bir açıklamam var (aşağıda özetlenen noktaları göstermek için çalışan örnek kod dahil): XML'i SQL Server'a eklerken "kodlama değiştirilemiyor" hatası nasıl çözülür , ancak temel bilgiler şunlardır:
- XML bildirimi isteğe bağlıdır
- XML veri türü dizeleri her zaman UCS-2 / UTF-16 LE olarak depolar
- XML'iniz UCS-2 / UTF-16 LE ise, o zaman siz:
- veriyi
NVARCHAR(MAX)
ya XML
/ SqlDbType.NVarChar
(maxsize = -1) ya SqlDbType.Xml
da ya da değişmez bir dize kullanılıyorsa, o zaman büyük harf "N" ile önek olarak verilmelidir.
- XML bildirimini belirtiyorsanız, "UCS-2" veya "UTF-16" olmalıdır (burada gerçek bir fark yoktur)
- XML'iniz 8 bit olarak kodlandıysa (ör. "UTF-8" / "iso-8859-1" / "Windows-1252"), o zaman siz:
- XML bildirimini belirtmeniz gerekir EĞER kodlama, veritabanının varsayılan Harmanlaması tarafından belirtilen kod sayfasından farklıysa
- gibi veri geçmelidir
VARCHAR(MAX)
/ SqlDbType.VarChar
(maxsize = 1), ya da hazır sonra bir dizi kullanarak eğer gerekiyor olmayan bir üst-durum "N" öneki ile.
- Hangi 8 bit kodlama kullanılırsa kullanılsın, XML bildiriminde belirtilen "kodlama", baytların gerçek kodlamasıyla eşleşmelidir.
- 8 bit kodlama, XML veri türü tarafından UTF-16 LE'ye dönüştürülür
Yukarıda özetlenen noktaları göz önünde bulundurarak ve .NET'teki dizelerin her zaman UTF-16 LE / UCS-2 LE olduğu göz önüne alındığında (kodlama açısından bunlar arasında hiçbir fark yoktur), sorularınızı yanıtlayabiliriz:
Daha sonra ona ihtiyaç duyduğumda bir Nesneyi serileştirmek için StringWriter kullanmamam için bir neden var mı?
Hayır, StringWriter
kodunuz gayet iyi görünüyor (en azından sorudaki 2. kod bloğunu kullanarak sınırlı testimde herhangi bir sorun görmüyorum).
Kodlamayı UTF-16 (xml etiketinde) olarak ayarlamak o zaman işe yaramaz mı?
XML bildirimini sağlamak gerekli değildir. Eksik olduğunda , dizeyi SQL Server'a NVARCHAR
(ie SqlDbType.NVarChar
) veya XML
(ie SqlDbType.Xml
) olarak iletirseniz kodlamanın UTF-16 LE olduğu varsayılır . Olarak iletilirse VARCHAR
(yani SqlDbType.VarChar
) kodlamanın varsayılan 8 bitlik Kod Sayfası olduğu varsayılır . Herhangi bir standart olmayan ASCII karakteriniz varsa (yani, 128 ve üzeri değerler) ve olarak geçiyorsanız VARCHAR
, büyük olasılıkla "?" BMP karakterleri ve "??" için Tamamlayıcı Karakterler için SQL Server, UTF-16 dizesini .NET'ten, UTF-16 / UCS-2'ye dönüştürmeden önce geçerli Veritabanının Kod Sayfasının 8 bitlik bir dizesine dönüştürecektir. Ancak herhangi bir hata almamalısınız.
Eğer XML bildirimi belirtirseniz Öte yandan, o zaman gerekir eşleşen 8-bit veya 16-bit veri türü kullanılarak SQL Server içine geçmektedir. Eğer bir deklarasyon kodlama olduğunu belirten varsa Yani ya UCS-2 veya UTF-16, o zaman gerekir olarak geçmek SqlDbType.NVarChar
ya SqlDbType.Xml
. Veya, kodlama (yani 8 bitlik seçeneklerden biri olduğunu belirten bir bildiri varsa UTF-8
, Windows-1252
, iso-8859-1
, vs), o zaman gerekir olarak geçmek SqlDbType.VarChar
. Bildirilen kodlamanın uygun 8 veya 16 bitlik SQL Server veri türü ile eşleştirilmemesi, aldığınız "kodlama değiştirilemiyor" hatasıyla sonuçlanacaktır.
Örneğin, sizin StringWriter
tabanlı serileştirme kodunuzu kullanarak , XML'nin ortaya çıkan dizesini yazdırdım ve SSMS'de kullandım. Aşağıda görebileceğiniz gibi (çünkü, XML bildirimi dahildir StringWriter
için bir seçenek yok OmitXmlDeclaration
gibi XmlWriter
çok uzun doğru SQL Server veri türü olarak dize geçerken hiçbir sorun teşkil yapar):
DECLARE @Xml XML = N'<?xml version="1.0" encoding="utf-16"?>
<string>Test ሴ😸</string>';
SELECT @Xml;
Gördüğünüz gibi ሴ
, BMP Kod Noktası U + 1234 ve 😸
Tamamlayıcı Karakter Kod Noktası U + 1F638 olduğu göz önüne alındığında , standart ASCII'nin ötesindeki karakterleri bile işler . Ancak, aşağıdakiler:
DECLARE @Xml XML = '<?xml version="1.0" encoding="utf-16"?>
<string>Test ሴ😸</string>';
aşağıdaki hataya neden olur:
Msg 9402, Level 16, State 1, Line XXXXX
XML parsing: line 1, character 39, unable to switch the encoding
Ergo, tüm bu açıklamalar bir yana, asıl sorunuzun tam çözümü:
İpleri açıkça geçiriyordun SqlDbType.VarChar
. Öğesine geçin SqlDbType.NVarChar
ve XML bildirimini kaldırmanın ekstra adımından geçmeye gerek kalmadan çalışacaktır. Bu, SqlDbType.VarChar
XML bildirimini tutmak ve kaldırmak yerine tercih edilir çünkü bu çözüm, XML standart olmayan ASCII karakterler içerdiğinde veri kaybını önleyecektir. Örneğin:
DECLARE @Xml2 XML = '<string>Test ሴ😸</string>';
SELECT @Xml2;
Gördüğünüz gibi bu sefer hata yok ama şimdi veri kaybı var 🙀.