我正在尝试在SQL Server 2016中创建一个存储过程,该存储过程会将先前转换为Varbinary
的XML转换回XML,但是转换时出现“非法XML字符”错误。我找到了一种似乎可行的解决方法,但实际上无法弄清楚为什么为什么起作用,这让我感到不舒服。
存储过程获取在SSIS中转换为二进制并插入表的varbinary(MAX)
列中的数据,并执行简单的
CAST(Column AS XML)
它工作了很长时间,当最初的XML开始包含®(注册商标)符号时,我才开始看到一个问题。
现在,当我尝试将二进制文件转换为XML时,出现此错误
9420州第1级第23行的消息9
XML解析:第1行,字符7,非法xml字符
但是,如果我先将二进制文件转换为varchar(MAX)
,然后再将其转换为XML,它似乎可以正常工作。我不了解执行中间的CAST与直接转换为XML不同时发生了什么。我主要担心的是,我不想将其添加到这种情况下,并且最终会带来意想不到的后果。
测试代码:
DECLARE @foo VARBINARY(MAX)
DECLARE @bar VARCHAR(MAX)
DECLARE @Nbar NVARCHAR(MAX)
--SELECT Varbinary
SET @foo = CAST( '<Test>®</Test>' AS VARBINARY(MAX))
SELECT @foo AsBinary
--select as binary as varchar
SET @bar = CAST(@foo AS VARCHAR(MAX))
SELECT @bar BinaryAsVarchar -- Correct string output
--select binary as nvarchar
SET @nbar = CAST(@foo AS NVARCHAR(MAX))
SELECT @nbar BinaryAsNvarchar -- Chinese characters
--select binary as XML
SELECT TRY_CAST(@foo AS XML) BinaryAsXML -- ILLEGAL XML character
-- SELECT CONVERT(xml, @obfoo) BinaryAsXML --ILLEGAL XML Character
--select BinaryAsVarcharAsXML
SELECT TRY_CAST(@bar AS XML) BinaryAsVarcharAsXML -- Correct Output
--select BinaryAsNVarcharAsXML
SELECT TRY_CAST(@nbar AS XML) BinaryAsNvarcharAsXML -- Chinese Characters
答案 0 :(得分:0)
有几件事要知道:
VARCHAR
,它是 1字节编码的扩展ASCII ,还有NVARCHAR
,它是UCS-2
(与utf-16
几乎一样)。 VARCHAR
将 plain latin 用于第一组字符,并将排序规则提供的代码页映射用于第二组。VARCHAR
不是utf-8 。 utf-8
与VARCHAR
一起使用,只要所有字符都以1字节为单位即可。但是utf-8
知道很多2字节编码(最多4字节编码)的字符,这会破坏VARCHAR
字符串的内部存储。NVARCHAR
将在本地几乎可以使用任何2字节编码字符(这意味着几乎可以使用任何现有字符)。但这不是完全utf-16
(存在3字节编码字符,这会破坏SQL Server的内部存储)。NVARCHAR
值的分层组织的物理表。 VARCHAR
字符串更糟。VARCHAR
字符串XML存储为VARBINARY
是对您不应该做的事情的累加。尝试一下:
DECLARE @text1Byte VARCHAR(100)='<test>blah</test>';
DECLARE @text2Byte NVARCHAR(100)=N'<test>blah</test>';
SELECT CAST(@text1Byte AS VARBINARY(MAX)) AS text1Byte_Binary
,CAST(@text2Byte AS VARBINARY(MAX)) AS text2Byte_Binary
,CAST(@text1Byte AS XML) AS text1Byte_XML
,CAST(@text2Byte AS XML) AS text2Byte_XML
,CAST(CAST(@text1Byte AS VARBINARY(MAX)) AS XML) AS text1Byte_XML_via_Binary
,CAST(CAST(@text2Byte AS VARBINARY(MAX)) AS XML) AS text2Byte_XML_via_Binary
您将看到的唯一区别是0x3C0074006500730074003E0062006C00610068003C002F0074006500730074003E00
中的许多零。这是由于nvarchar
的 2字节编码,此示例中不需要每个第二字节。但是,如果您需要远东字符,则情况将完全不同。
它起作用的原因:SQL-Server非常聪明。如引擎所知,从变量到XML的转换非常简单,基础变量是varchar
或nvarchar
。但是最后两个演员不同。引擎必须检查二进制文件是否有效nvarchar
,如果失败,将再次尝试使用varchar
。
现在尝试将您的注册商标添加到给定的示例中。首先将其添加到第二个变量DECLARE @text2Byte NVARCHAR(100)=N'<test>blah®</test>';
中,然后尝试运行它。然后将其添加到第一个变量,然后重试。
您可以尝试的方法:
将二进制文件先转换为varchar(max)
,然后转换为nvarchar(max)
,最后转换为xml
。
,CAST(CAST(CAST(CAST(@text1Byte AS VARBINARY(MAX)) AS VARCHAR(MAX)) AS NVARCHAR(MAX)) AS XML) AS text1Byte_XML_via_Binary
这可以工作,但是不会很快...