我正在尝试将varchar列中的数据转换为XML,但我遇到了某些字符的错误。跑这个......
-- This fails
DECLARE @Data VARCHAR(1000) = '<?xml version="1.0" encoding="utf-8"?><NewDataSet>Test¦</NewDataSet>';
SELECT CAST(@Data AS XML) AS DataXml
...导致以下错误
Msg 9420,Level 16,State 1,Line 3
XML解析:第1行,第55个字符,非法xml字符
看起来它是导致错误的破坏管道字符,但我认为它是UTF-8的有效字符。查看XML spec它似乎有效。
当我把它改成这个......
-- This works
DECLARE @Data VARCHAR(1000) = '<?xml version="1.0" encoding="utf-8"?><NewDataSet>Test¦</NewDataSet>';
SELECT CAST(REPLACE(CAST(@Data AS NVARCHAR(MAX)), 'encoding="utf-8"', '') AS XML) AS DataXml
...它没有错误(将编码字符串替换为utf-16也有效)。我正在使用带有SQL_Latin1_General_CP1_CI_AS Coallation的SQL Server 2008 R2。
任何人都可以告诉我为什么我需要转换为NVARCHAR
并剥离encoding="utf-8"
才能生效吗?
谢谢,
修改
看来这也有效......
DECLARE @Data VARCHAR(1000) = '<?xml version="1.0" encoding="utf-8"?><NewDataSet>Test¦</NewDataSet>';
SELECT CAST(REPLACE(@Data, 'encoding="utf-8"', '') AS XML) AS DataXml
从prolog中删除utf-8编码足以让SQL Server进行转换。
答案 0 :(得分:3)
您的管道字符使用Unicode代码点U+00A6 BROKEN BAR
而不是U+007C VERTICAL LINE
。 U+00A6
超出ASCII。 VARCHAR
不支持非ASCII字符。这就是为什么你必须使用NVARCHAR
代替,它是为处理Unicode数据而设计的。
答案 1 :(得分:0)
不幸的是,所接受的答案是103.3333333%错误。 VARCHAR
绝对支持扩展ASCII。标准ASCII只是前128个值(0x00-0x7F)。对于SQL Server中的所有代码页(即8位VARCHAR
数据)和UTF-16(即16位NVARCHAR
数据),情况恰好相同。扩展ASCII覆盖256个总值(0x80-0xFF)中的剩余128个。每个代码页的128个值/代码点不同,尽管其中一些之间有很多重叠。
接受的答案指出VARCHAR
不支持U+00A6 BROKEN BAR
。只需在第一行之后添加SELECT @Data;
即可轻松证明这一点:
DECLARE @Data VARCHAR(1000) =
'<?xml version="1.0" encoding="utf-8"?><NewDataSet>Test¦</NewDataSet>';
SELECT @Data;
返回:
<?xml version="1.0" encoding="utf-8"?><NewDataSet>Test¦</NewDataSet>
明确支持¦
字符,因此问题可能出在其他地方。
似乎是导致错误的竖线字符,但我认为这是UTF-8的有效字符。
破折号是UTF-8中的有效字符。问题是:您没有传递UTF-8数据。是的,您声明xml声明中的编码为UTF-8,但这并不意味着数据是 UTF-8,它只是将期望值设置为UTF-8
您正在将VARCHAR
文字转换为XML。数据库的默认归类为SQL_Latin1_General_CP1_CI_AS
,它使用Windows-1252代码页存储VARCHAR
数据。这意味着折断的竖线字符的值为166或0xA6。好吧,0xA6不是有效的UTF-8编码的任何东西。如果您确实要传递UTF-8编码的数据,那么折断的竖线字符将是两个字节:0xC2和0xA6。如果将0xC2字节添加到原始输入值(0xA6相同,那么我们可以将其保留在原来的位置),我们得到:
DECLARE @Data VARCHAR(1000) = '<?xml version="1.0" encoding="utf-8"?><NewDataSet>Test'
+ CHAR(0xC2) + '¦</NewDataSet>';
SELECT @Data AS [@Data];
SELECT CAST(@Data AS XML) AS [DataXml];
并返回:
<?xml version="1.0" encoding="utf-8"?><NewDataSet>Test¦</NewDataSet>
其次:
<NewDataSet>Test¦</NewDataSet>
这就是为什么删除encoding="utf-8"
可以解决问题的原因:
VARCHAR
,这意味着编码是与字符串的排序规则关联的代码页,以及{{1 }}文字或变量使用数据库的默认排序规则。意味着,在这种情况下,如果没有VARCHAR
,或带有encoding="xxxxxx"
的,则字节将需要编码为Windows-1252,而实际上它们是。 >
将所有内容放在一起,我们得到:
如果您有实际的UTF-8编码字符串,则可以将其 传递到XML数据类型中,但是您需要:
encoding="Windows-1252"
变量或列用于包含字符串如果您在代码页中编码的字符串与数据库的默认排序规则相关联,则您需要:
NVARCHAR
变量或列用于包含字符串NVARCHAR
声明的一部分进行“编码”,或者没有将编码设置为与数据库的默认排序规则关联的代码页(例如,代码页1252的<?xml ?>
)如果您的字符串已经是Unicode,则需要:
Windows-1252
变量或列NVARCHAR
声明中没有“编码”,或者编码设置为“ utf-16” 有关此详细信息,请参见我对“ Converting accented characters in varchar() to XML causing “illegal XML character””的回答。
有关归类,字符编码等的信息,请访问:Collations Info