我目前正在寻找一种简单的方法来序列化对象(在C#3中)。
我在Google上搜索了一些示例,并提出了类似的内容:
MemoryStream memoryStream = new MemoryStream ( ); XmlSerializer xs = new XmlSerializer ( typeof ( MyObject) ); XmlTextWriter xmlTextWriter = new XmlTextWriter ( memoryStream, Encoding.UTF8 ); xs.Serialize ( xmlTextWriter, myObject); string result = Encoding.UTF8.GetString(memoryStream .ToArray());
看完这个问题后,我问自己,为什么不使用StringWriter?似乎容易得多。
XmlSerializer ser = new XmlSerializer(typeof(MyObject)); StringWriter writer = new StringWriter(); ser.Serialize(writer, myObject); serializedValue = writer.ToString();
另一个问题是,第一个示例生成的XML我不能只写到SQL Server 2005 DB的XML列中。
第一个问题是:是否有一个原因,当我以后需要它作为字符串时,不应该使用StringWriter序列化对象?谷歌搜索时,我从未使用StringWriter找到结果。
第二个是当然的:如果您不应该使用StringWriter(出于任何原因)这样做,那将是一种正确的好方法吗?
加成:
正如两个答案都已经提到的那样,我将进一步探讨XML to DB问题。
写入数据库时,出现以下异常:
System.Data.SqlClient.SqlException:XML解析:第1行,字符38,无法切换编码
对于字符串
<?xml version="1.0" encoding="utf-8"?><test/>
我把从XmlTextWriter创建的字符串作为xml放在那里。这一项不起作用(手动插入数据库均无效)。
之后,我尝试使用encoding =“ utf-16”手动插入(只写INSERT INTO …),但同样失败。完全删除编码就可以了。在该结果之后,我切换回StringWriter代码,瞧-它起作用了。
问题:我真的不明白为什么。
Christian Hayter的文章:通过这些测试,我不确定是否必须使用utf-16写入数据库。这样,将编码设置为UTF-16(在xml标记中)就不会起作用吗?
< TL; DR>问题实际上很简单:您没有将声明的编码(在XML声明中)与输入参数的数据类型匹配。如果您是手动添加<?xml version="1.0" encoding="utf-8"?><test/>到字符串中,则声明的SqlParameter类型为SqlDbType.Xml或SqlDbType.NVarChar会给您“无法切换编码”错误。然后,当通过T- SQL手动插入时,由于将声明的编码切换为utf-16,因此您显然插入了一个VARCHAR字符串(不以大写字母“ N”作为前缀,因此是8位编码,例如UTF-8)。而不是NVARCHAR字符串(以大写字母“ N”为前缀,因此为16位UTF-16 LE编码)。
SqlParameter
SqlDbType.Xml
SqlDbType.NVarChar
utf-16
VARCHAR
NVARCHAR
该修复程序应该很简单:
encoding="utf-8"
encoding="utf-16"
SqlDbType.VarChar
(详细回复如下)
这里的所有答案都过于复杂和不必要(无论克里斯蒂安和乔恩分别获得121和184的赞成票)。他们可能会提供有效的代码,但没有一个人真正回答问题。问题在于,没有人真正地理解这个问题,最终是关于SQL Server中XML数据类型如何工作的问题。这两个显然很聪明的人并不反对,但是这个问题与序列化到XML几乎没有关系。将XML数据保存到SQL Server中比这里所隐含的要容易得多。
只要您遵循如何在SQL Server中创建XML数据的规则,XML的产生方式实际上并不重要。在以下问题的答案中,我得到了更详尽的解释(包括工作示例代码,以说明以下要点):在将XML插入SQL Server时,如何解决“无法切换编码”错误,但是基础是:
NVARCHAR(MAX)
XML
VARCHAR(MAX)
有了上面的概述考虑点, 并 考虑到在.NET字符串 总是 UTF-16 LE / UCS-2 LE(有编码的那些方面没有区别),我们可以回答您的问题:
为什么在以后需要字符串时不使用StringWriter序列化对象是有原因的吗?
不,您的StringWriter代码看起来还不错(至少在使用问题的第二个代码块进行的有限测试中,我看不到任何问题)。
StringWriter
这样,将编码设置为UTF-16(在xml标记中)就不会起作用吗?
无需提供XML声明。如果缺少该字符串, 则如果 将字符串作为NVARCHAR(即SqlDbType.NVarChar)或XML(即SqlDbType.Xml)传递给SQL Server,则假定编码为UTF-16 LE 。如果以VARCHAR(即SqlDbType.VarChar)传入,则假定编码为默认的8位代码页。如果您有任何非标准ASCII字符(即值128和更高),并且以传入VARCHAR,那么您可能会看到“?” 用于BMP字符和“ ??” SQL Server将把.NET中的UTF-16字符串转换为当前数据库代码页的8位字符串,然后再将其转换回UTF-16 / UCS-2。但是您不应该得到任何错误。
另一方面,如果确实指定XML声明,则 必须 使用匹配的8位或16位数据类型传递到SQL Server。因此,如果您有声明说明编码为UCS-2或UTF-16,则 必须 以SqlDbType.NVarChar或形式传入SqlDbType.Xml。或者,如果你有一个声明,表示编码是8位的选项之一(即UTF-8,Windows-1252,iso-8859-1等等),那么你 必须 在为合格SqlDbType.VarChar。无法将声明的编码与正确的8位或16位SQL Server数据类型匹配,将导致您收到“无法切换编码”错误。
UTF-8
Windows-1252
iso-8859-1
例如,使用StringWriter基于您的序列化代码,我只打印了XML的结果字符串,并将其用于SSMS。如下所示,其中包含XML声明(因为StringWriter没有OmitXmlDeclaration类似的选项XmlWriter),只要您将字符串作为正确的SQL Server数据类型传递,就不会出现问题:
OmitXmlDeclaration
XmlWriter
-- 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>
如您所见,鉴于ሴBMP代码点U + 1234和😸补充字符代码点U + 1F638 ,它甚至可以处理超出标准ASCII的字符。但是,以下内容:
ሴ
😸
-- No upper-case "N" prefix on the string literal, hence VARCHAR: DECLARE @Xml XML = '<?xml version="1.0" encoding="utf-16"?> <string>Test ሴ😸</string>';
导致以下错误:
Msg 9402, Level 16, State 1, Line XXXXX XML parsing: line 1, character 39, unable to switch the encoding
因此,除了所有这些解释之外,您原来的问题的完整解决方案是:
您显然是将字符串传递为SqlDbType.VarChar。切换到SqlDbType.NVarChar,它将无需删除XML声明的额外步骤即可工作。与保留SqlDbType.VarChar和删除XML声明相比,这是首选方法,因为当XML包含非标准ASCII字符时,此解决方案将防止数据丢失。例如:
-- 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>
如您所见,这次没有错误,但是现在有数据丢失🙀。