在将XML插入SQL Server时如何解决“无法切换编码”错误

我试图插入XML列(SQL SERVER 2008 R2),但服务器的抱怨:

System.Data.SqlClient.SqlException(0x80131904):
XMLparsing:行1,字符39,无法切换编码

我发现为了插入成功,XML列必须是UTF-16。

我使用的代码是:

XmlSerializer serializer = new XmlSerializer(typeof(MyMessage)); StringWriter str = new StringWriter(); serializer.Serialize(str, message); string messageToLog = str.ToString(); 

我如何序列化对象是UTF-8string?

编辑 :好的,抱歉的混合 – string需要在UTF-8。 你是对的 – 默认情况下是UTF-16,如果我尝试以UTF-8插入,它会通过。 所以问题是如何序列化成UTF-8。

尝试插入到SQL Server时,这会导致错误:

  <?xml version="1.0" encoding="utf-16"?> <MyMessage>Teno</MyMessage> 

这不:

  <?xml version="1.0" encoding="utf-8"?> <MyMessage>Teno</MyMessage> 

更新

我想出了当它的Xml列types的SQL Server 2008需要utf-8时,以及当你尝试插入的xml规范的encoding属性中的utf-16:

当你想添加utf-8 ,然后像下面这样向SQL命令添加参数:

  sqlcmd.Parameters.Add("ParamName", SqlDbType.VarChar).Value = xmlValueToAdd; 

如果您尝试在上一行中添加encoding=utf-16的xmlValueToAdd,则会在插入时产生错误。 另外, VarChar意味着国家字符不被识别(它们被当作问号)。

要将utf-16添加到db,可以在前面的示例中使用SqlDbType.NVarCharSqlDbType.Xml ,或者根本不指定types:

  sqlcmd.Parameters.Add(new SqlParameter("ParamName", xmlValueToAdd)); 

尽pipe.netstring总是UTF-16您需要使用UTF-16编码序列化对象。 这应该是这样的:

 public static string ToString(object source, Type type, Encoding encoding) { // The string to hold the object content String content; // Create a memoryStream into which the data can be written and readed using (var stream = new MemoryStream()) { // Create the xml serializer, the serializer needs to know the type // of the object that will be serialized var xmlSerializer = new XmlSerializer(type); // Create a XmlTextWriter to write the xml object source, we are going // to define the encoding in the constructor using (var writer = new XmlTextWriter(stream, encoding)) { // Save the state of the object into the stream xmlSerializer.Serialize(writer, source); // Flush the stream writer.Flush(); // Read the stream into a string using (var reader = new StreamReader(stream, encoding)) { // Set the stream position to the begin stream.Position = 0; // Read the stream into a string content = reader.ReadToEnd(); } } } // Return the xml string with the object content return content; } 

通过将编码设置为Encoding.Unicode,不仅string将是UTF-16而且还应该以UTF-16格式获取xmlstring。

 <?xml version="1.0" encoding="utf-16"?> 

这个问题是另外两个重复的问题,令人吃惊的是 – 虽然这是最近的一个,但我相信它缺less了最好的答案。

重复的,我认为是他们最好的答案是:

最后,只要XmlReader可以在应用程序服务器本地parsing它,那么声明或使用什么编码就没有关系。

正如在SQL服务器中从XMLtypes列的ADO.net中读取XML的最有效方式所证实的那样? ,SQL Server以有效的二进制格式存储XML。 通过使用SqlXml类,ADO.net可以以这种二进制格式与SQL Server进行通信,而不需要数据库服务器对XML进行任何序列化或反序列化。 这对于通过networking传输也应该更有效率。

通过使用SqlXml ,XML将被预先parsing到数据库,然后DB不需要知道任何有关字符编码的内容 – UTF-16或其他。 尤其要注意的是,XML声明甚至不会被数据库中的数据持久化,无论使用哪种方法来插入它。

对于看起来非常类似的方法,请参考上面链接的答案,但这个例子是我的:

 using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.IO; using System.Xml; static class XmlDemo { static void Main(string[] args) { using(SqlConnection conn = new SqlConnection()) { conn.ConnectionString = "..."; conn.Open(); using(SqlCommand cmd = new SqlCommand("Insert Into TestData(Xml) Values (@Xml)", conn)) { cmd.Parameters.Add(new SqlParameter("@Xml", SqlDbType.Xml) { // Works. // Value = "<Test/>" // Works. XML Declaration is not persisted! // Value = "<?xml version=\"1.0\"?><Test/>" // Works. XML Declaration is not persisted! // Value = "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Test/>" // Error ("unable to switch the encoding" SqlException). // Value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>" // Works. XML Declaration is not persisted! Value = new SqlXml(XmlReader.Create(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>"))) }); cmd.ExecuteNonQuery(); } } } } 

请注意,我不认为最后一个(未评论)的例子是“生产就绪”的,但保持简洁和可读性。 如果正确完成,那么StringReader和创build的XmlReader都应该在using语句中进行初始化,以确保在完成时调用它们的Close()方法。

从我所看到的,XML声明在使用XML列时永远不会持久化。 即使不使用.NET,也只是使用这个直接的SQL插入语句,例如,XML声明不会用XML保存到数据库中:

 Insert Into TestData(Xml) Values ('<?xml version="1.0" encoding="UTF-8"?><Test/>'); 

现在根据OP的问题,被序列化的对象仍然需要从MyMessage对象转换成XML结构,而XmlSerializer仍然是需要的。 然而,在最坏的情况下,而不是序列化到一个string,消息可以被序列化到一个XmlDocument – 然后可以通过一个新的XmlNodeReader传递给SqlXml – 避免反序列化/串行化到一个string。 (有关详细信息和示例,请参阅http://blogs.msdn.com/b/jongallant/archive/2007/01/30/how-to-convert-xmldocument-to-xmlreader-for-sqlxml-data-type.aspx 。)

这里的一切都是针对.NET 4.0和SQL Server 2008 R2开发的。

请不要通过额外的转换(去反序列化和序列化 – DOM,string或其他)来运行XML,正如其他答案中所示。

是不是告诉序列化程序不要输出XML声明的最简单的解决scheme? .NET和SQL应该在它们之间sorting。

  XmlSerializer serializer = new XmlSerializer(typeof(MyMessage)); StringWriter str = new StringWriter(); using (XmlWriter writer = XmlWriter.Create(str, new XmlWriterSettings { OmitXmlDeclaration = true })) { serializer.Serialize(writer, message); } string messageToLog = str.ToString(); 

一个string在.NET中总是UTF-16,所以只要你留在你的托pipe应用程序中,你不必关心它是哪种编码。

问题更可能发生在与SQL服务器交谈的地方。 你的问题不显示代码,所以很难指出确切的错误。 我的build议是检查是否有一个属性或属性,您可以设置该代码,指定发送到服务器的数据的编码。

我永远花了我的时间来重新解决这个问题。

我正在向SQL Server中执行一个INSERT语句,如下所示:

 UPDATE Customers SET data = '<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>'; 

这给出了错误:

Msg 9402,Level 16,State 1,Line 2
XMLparsing:行1,字符39,无法切换编码

而真正的,非常简单的解决方法是:

 UPDATE Customers SET data = N'<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>'; 

区别在于Unicodestring前缀为N

N '<?xml version =“1.0”encoding =“utf-16”?> Teno </ MyMessage>'

在前一种情况下,一个前缀不固定的string被认为是varchar(例如Windows-1252代码页)。 当它在string中遇到encoding="utf-16" ,就会产生冲突(正确,因为string不是 utf-16)。

解决的办法是将string作为nvarchar (即UTF-16)传递给SQL服务器:

N '<?xml version =“1.0”encoding =“utf-16”?>'

这样的string UTF-16,它与XML所说的UTF-16编码相匹配。 可以这么说,地毯与窗帘相配。

你正在序列化一个string而不是一个字节数组,所以在这一点上,任何编码还没有发生。

“messageToLog”的开头是什么样的? XML是否指定了一个编码(例如utf-8),后来事实certificate是错误的?

编辑

根据你的进一步的信息,这听起来像string被传递到数据库时自动转换为utf-8,但数据库扼stream器,因为XML声明说它是utf-16。

在这种情况下,你不需要序列化到utf-8。 您需要使用从XML中省略的“encoding =”进行序列化。 XmlFragmentWriter(不是.Net的标准部分,Google它)可以让你做到这一点。

xml序列化程序的默认编码应该是UTF-16。 只是为了确保你可以尝试 –

 XmlSerializer serializer = new XmlSerializer(typeof(YourObject)); // create a MemoryStream here, we are just working // exclusively in memory System.IO.Stream stream = new System.IO.MemoryStream(); // The XmlTextWriter takes a stream and encoding // as one of its constructors System.Xml.XmlTextWriter xtWriter = new System.Xml.XmlTextWriter(stream, Encoding.UTF16); serializer.Serialize(xtWriter, yourObjectInstance); xtWriter.Flush();