vel*_*koz 43 .net xml sql-server utf-8 utf-16
我正在尝试插入XML列(SQL SERVER 2008 R2),但服务器抱怨:
System.Data.SqlClient.SqlException(0x80131904):
XML解析:第1行,第39个字符,无法切换编码
我发现XML列必须是UTF-16才能使插入成功.
我正在使用的代码是:
XmlSerializer serializer = new XmlSerializer(typeof(MyMessage));
StringWriter str = new StringWriter();
serializer.Serialize(str, message);
string messageToLog = str.ToString();
Run Code Online (Sandbox Code Playgroud)
如何将对象序列化为UTF-8字符串?
编辑:好的,抱歉混淆 - 字符串需要是UTF-8.你是对的 - 默认情况下它是UTF-16,如果我尝试以UTF-8插入它就会通过.所以问题是如何序列化为UTF-8.
例
这会在尝试插入SQL Server时导致错误:
<?xml version="1.0" encoding="utf-16"?>
<MyMessage>Teno</MyMessage>
Run Code Online (Sandbox Code Playgroud)
这不是:
<?xml version="1.0" encoding="utf-8"?>
<MyMessage>Teno</MyMessage>
Run Code Online (Sandbox Code Playgroud)
更新
我想出当SQL Server 2008的Xml列类型需要utf-8时,以及当encoding你尝试插入xml规范的属性中的utf-16时:
如果要添加utf-8,请将参数添加到SQL命令,如下所示:
sqlcmd.Parameters.Add("ParamName", SqlDbType.VarChar).Value = xmlValueToAdd;
Run Code Online (Sandbox Code Playgroud)
如果您尝试encoding=utf-16在上一行中添加xmlValueToAdd,则会在插入中产生错误.此外,VarChar意味着国家字符不被识别(它们变成问号).
要将utf-16添加到db,请使用SqlDbType.NVarChar或SqlDbType.Xml在前面的示例中,或者根本不指定type:
sqlcmd.Parameters.Add(new SqlParameter("ParamName", xmlValueToAdd));
Run Code Online (Sandbox Code Playgroud)
zie*_*mer 34
这个问题几乎与其他2个问题重复,令人惊讶的是 - 虽然这个问题是最新的 - 但我相信它缺少最佳答案.
重复,以及我认为最佳答案,是:
最后,只要XmlReader可以在应用程序服务器中本地解析它,无论声明或使用什么编码都无关紧要.
正如在SQL Server中从XML类型列中读取ADO.net中的XML的最有效方式所证实的那样?,SQL Server以高效的二进制格式存储XML.通过使用SqlXml该类,ADO.net可以以此二进制格式与SQL Server通信,而不需要数据库服务器对XML进行任何序列化或反序列化.这对于通过网络传输也应该更有效.
通过使用SqlXml,XML将被预先解析到数据库,然后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();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我不会将最后一个(未注释的)示例视为"生产就绪",而是将其保留为简洁易读.如果正确完成,则应在语句中初始化the StringReader和created,以确保在完成时调用其方法.XmlReaderusingClose()
从我所看到的,使用XML列时,XML声明永远不会持久化.例如,即使不使用.NET并仅使用此直接SQL插入语句,XML声明也不会使用XML保存到数据库中:
Insert Into TestData(Xml) Values ('<?xml version="1.0" encoding="UTF-8"?><Test/>');
Run Code Online (Sandbox Code Playgroud)
现在就OP的问题而言,要序列化的对象仍然需要从MyMessage对象转换为XML结构,并且XmlSerializer仍然需要这样做.然而,在最坏的情况,而不是序列化到一个字符串,该消息可以改为被序列化到一个XmlDocument-其然后可以被传递到SqlXml通过一个新的XmlNodeReader-避免反序列化/串行化行程为字符串.(有关详细信息和示例,请参阅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,字符串或其他方式)运行XML来消除浪费,如此处和其他地方的其他答案所示.
小智 21
虽然.net字符串始终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;
}
Run Code Online (Sandbox Code Playgroud)
通过将编码设置为Encoding.Unicode,不仅字符串将是,UTF-16但您还应该获取xml字符串UTF-16.
<?xml version="1.0" encoding="utf-16"?>
Run Code Online (Sandbox Code Playgroud)
bat*_*wad 12
告诉序列化程序不要输出XML声明是不是最简单的解决方案?.NET和SQL应该将它们之间的其余部分排序.
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();
Run Code Online (Sandbox Code Playgroud)
我花了很长时间才重新解决这个问题.
我正在对INSERTSQL Server 做一个声明,如:
UPDATE Customers
SET data = '<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';
Run Code Online (Sandbox Code Playgroud)
这给出了错误:
消息9402,级别16,状态1,行2
XML解析:第1行,字符39,无法切换编码
真正的,非常简单的解决方案是:
UPDATE Customers
SET data = N'<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';
Run Code Online (Sandbox Code Playgroud)
区别在于Unicode字符串前缀为N:
N '<?xml version ="1.0"encoding ="utf-16"?> Teno </ MyMessage>'
在前一种情况下,假定未加前缀的字符串是varchar(例如,Windows-1252代码页).当它遇到encoding="utf-16"字符串内部时,就会发生冲突(正确的是,因为字符串不是 utf-16).
修复是将字符串作为nvarchar(即UTF-16)传递给SQL服务器:
N '<?xml version ="1.0"encoding ="utf-16"?>'
这样字符串就是 UTF-16,它匹配XML所说的utf-16编码.可以这么说,地毯与窗帘相配.
@ziesemer 的回答(上图)是该问题的唯一完全正确答案以及该问题的链接副本。但是,它仍然可以使用更多的解释和一些澄清。将此视为@ziesemer 答案的扩展。
即使他们产生了预期的结果,这个问题的大多数答案(包括重复问题)都是错综复杂的,并且要经过许多不必要的步骤。这里的主要问题是对XML数据类型在 SQL Server 中的实际工作方式总体缺乏了解(鉴于它没有得到很好的记录,这并不奇怪)。该XML类型:
msdn站点的某处记录)。优化包括:
<ElementName>...</ElementName>" 以字符串形式占用 27 个字符(即 54 个字节),但在XML类型中存储时仅占用 11 个字符(即 22 个字节)。这是它的单个实例。多个实例占用 54 字节的额外倍数。但是在 XML 类型中,每个实例只占用那个数字 ID 的空间,很可能是一个 4 字节的 int。可以传入 8 位/非 UTF-16 数据。在这种情况下,您需要确保该字符串不是一个NVARCHAR字符串(即不以大写字母“N”为前缀,而不是声明为NVARCHARwhen处理 T-SQL 变量,而不是SqlDbType.NVarChar在 .NET 中声明)。并且,您需要确保您确实有XML声明,并且它指定了正确的编码。
PRINT 'VARCHAR / UTF-8:';
DECLARE @XML_VC_8 XML;
SET @XML_VC_8 = '<?xml version="1.0" encoding="utf-8"?><test/>';
PRINT 'Success!'
-- Success!
GO
PRINT '';
PRINT 'NVARCHAR / UTF-8:';
DECLARE @XML_NVC_8 XML;
SET @XML_NVC_8 = N'<?xml version="1.0" encoding="utf-8"?><test/>';
PRINT 'Success!'
/*
Msg 9402, Level 16, State 1, Line XXXXX
XML parsing: line 1, character 38, unable to switch the encoding
*/
GO
PRINT '';
PRINT 'VARCHAR / UTF-16:';
DECLARE @XML_VC_16 XML;
SET @XML_VC_16 = '<?xml version="1.0" encoding="utf-16"?><test/>';
PRINT 'Success!'
/*
Msg 9402, Level 16, State 1, Line XXXXX
XML parsing: line 1, character 38, unable to switch the encoding
*/
GO
PRINT '';
PRINT 'NVARCHAR / UTF-16:';
DECLARE @XML_NVC_16 XML;
SET @XML_NVC_16 = N'<?xml version="1.0" encoding="utf-16"?><test/>';
PRINT 'Success!'
-- Success!
Run Code Online (Sandbox Code Playgroud)
如您所见,当输入字符串为 时NVARCHAR,则可以包含XML 声明,但必须为“UTF-16”。
当输入字符串VARCHAR为时,可以包含XML 声明,但不能是“UTF-16”。但是,它可以是任何有效的 8 位编码,在这种情况下,该编码的字节将转换为 UTF-16,如下所示:
DECLARE @XML XML;
SET @XML = '<?xml version="1.0" encoding="utf-8"?><test attr="'
+ CHAR(0xF0) + CHAR(0x9F) + CHAR(0x98) + CHAR(0x8E) + '"/>';
SELECT @XML;
-- <test attr="" />
SET @XML = '<?xml version="1.0" encoding="Windows-1255"?><test attr="'
+ CONVERT(VARCHAR(10), 0xF9ECE5ED) + '"/>';
SELECT @XML AS [XML from Windows-1255],
CONVERT(VARCHAR(10), 0xF9ECE5ED) AS [Latin1_General / Windows-1252];
/*
XML from Windows-1255 Latin1_General / Windows-1252
<test attr="????" /> ùìåí
*/
Run Code Online (Sandbox Code Playgroud)
第一个示例为Smiling Face with Sunglasses指定了 4 字节的 UTF-8 序列,并且它被正确转换。
第二个示例使用 4 个字节来表示组成单词“Shalom”的 4 个希伯来字母,它被正确转换,并正确显示,因为第一个“F9”字节?是右侧的字符(因为希伯来语是从右到左的语言)。然而ùìåí,由于当前 DB 的默认排序规则是Latin1_General_100_CS_AS_SC.
在.NET中,字符串始终是UTF-16,因此只要您留在托管应用程序中,就不必关心它是哪种编码。
与SQL Server对话的地方更可能出现问题。您的问题没有显示该代码,因此很难确定确切的错误。我的建议是检查是否可以在该代码上设置一个属性或属性,以指定发送到服务器的数据的编码。
| 归档时间: |
|
| 查看次数: |
70014 次 |
| 最近记录: |