J S*_*dhu 3 xml sql-server encoding json utf-8
背景 - 我从网站接收以 UTF-8 编码的 json 格式的响应数据。json 的 body 属性具有 base64binary 类型的值,我将其存储为 ms sql server 上的 nvarchar 类型。
当我将 base64binary 数据转换为 varchar 或 nvarchar 时,我看到有趣的字符(代替双引号)表明存在编码问题 -这就是我问这个问题的原因。
请参阅下面的剖析代码和底部的可运行示例,了解我的担忧。
在转换过程中注意有趣的字符。
例如。代表IRB控股公司(的“公司“)
以下查询修复了上述问题 - 我看到引号应该出现,但随后在包含'&' 的行上失败,这是 xml 中的特殊字符。
select convert(xml, '<?xml version="1.0" encoding="UTF-8"?>' + convert(varchar(max),cast('' as xml).value('xs:base64Binary(sql:column("body"))','varbinary(max)')))
Run Code Online (Sandbox Code Playgroud)
以下查询通过使用replace语句处理上述问题,我能够按预期完全查看所有行。但是这个解决方案只会处理'&'s。
要运行的示例代码:
declare @t table ( [body] nvarchar(max) )
insert into @t(body)
select 'REFMTEFTLCBUWCDigJMgTWF5IDcsIDIwMTkg4oCTIENvdmV5ICYgUGFyayBFbmVyZ3kgSG9sZGluZ3MgTExDICjigJxDb3ZleSBQYXJr4oCdIA=='
select convert(varchar(max),cast('' as xml).value('xs:base64Binary(sql:column("body"))','varbinary(max)'))
, convert(xml, '<?xml version="1.0" encoding="UTF-8"?>'+ replace(convert(varchar(max),convert(varchar(max),cast('' as xml).value('xs:base64Binary(sql:column("body"))','varbinary(max)'))),'&','&'))
from @t
Run Code Online (Sandbox Code Playgroud)
问题- 我是否必须为其他 xml 特殊字符添加更多替换语句 - < , >?
试试这个功能
\nCREATE FUNCTION dbo.Convert_utf8(@utf8 VARBINARY(MAX))\nRETURNS NVARCHAR(MAX)\nAS\nBEGIN\n DECLARE @rslt NVARCHAR(MAX);\n\n SELECT @rslt=\n CAST(\n --\'<?xml version="1.0" encoding="UTF-8"?><![CDATA[\'\n 0x3C3F786D6C2076657273696F6E3D22312E302220656E636F64696E673D225554462D38223F3E3C215B43444154415B\n --the content goes within CDATA\n + @utf8\n --\']]>\'\n + 0x5D5D3E\n AS XML).value(\'.\', \'nvarchar(max)\');\n\n RETURN @rslt;\nEND\nGO\nRun Code Online (Sandbox Code Playgroud)\n并这样称呼它
\nSELECT *\n ,dbo.Convert_utf8(CAST(t.body AS XML).value(\'.\',\'varbinary(max)\'))\nFROM @t t;\nRun Code Online (Sandbox Code Playgroud)\n结果是
\nDALLAS, TX \xe2\x80\x93 May 7, 2019 \xe2\x80\x93 Covey & Park Energy Holdings LLC (\xe2\x80\x9cCovey Park\xe2\x80\x9d \nRun Code Online (Sandbox Code Playgroud)\nGSerg,非常感谢你!下面为您解答。我尝试并简化了它以在 UDF 中工作。
\n看起来好像 avarbinary(max)到 XML 的转换完全在 CLR 环境中完成,其中考虑了 XML 的编码声明。这似乎也适用于其他编码,但我现在没有时间来一般性地测试它。
因为它包含一些有关字符串编码的背景知识,可能值得阅读。
\n我简化了你的代码:
\ndeclare @t table ( [body] nvarchar(max) ) \n\ninsert into @t(body) \nselect \'REFMTEFTLCBUWCDigJMgTWF5IDcsIDIwMTkg4oCTIENvdmV5ICYgUGFyayBFbmVyZ3kgSG9sZGluZ3MgTExDICjigJxDb3ZleSBQYXJr4oCdIA==\';\n\nSELECT CAST(t.body AS XML).value(\'.\',\'varbinary(max)\')\n ,CAST(CAST(t.body AS XML).value(\'.\',\'varbinary(max)\') AS VARCHAR(MAX))\nFROM @t t;\nRun Code Online (Sandbox Code Playgroud)\n你会看到这个结果
\n0x44414C4C41532C20545820E28093204D617920372C203230313920E2809320436F7665792026205061726B20456E6572677920486F6C64696E6773204C4C432028E2809C436F766579205061726BE2809D20 \nDALLAS, TX \xc3\xa2\xe2\x82\xac\xe2\x80\x9c May 7, 2019 \xc3\xa2\xe2\x82\xac\xe2\x80\x9c Covey & Park Energy Holdings LLC (\xc3\xa2\xe2\x82\xac\xc5\x93Covey Park\xc3\xa2\xe2\x82\xac \nRun Code Online (Sandbox Code Playgroud)\n我会将第一个字符放置得对读者更友好
\n0x44414C4C41532C20545820E28093 \n D A L L A S , T X \xc3\xa2 \xe2\x82\xac \xe2\x80\x9c \nRun Code Online (Sandbox Code Playgroud)\nThe0x44是 the D,两倍 the0x4C是两倍LL,在空格之后0x20我们得到E28093。这是短划线的 3 字节编码代码点。SQL-Server 不会帮助你解决这个问题...它会将其解释为 3 个字符,每个字符 1 个字节...
恐怕你运气不好......
\nSQL-Server 不支持utf-8字符串。对启用来自文件系统的输入的支持有限,但其中的BCP / BULK字符串必须是两个受支持的选项之一: T-SQL
(var)char,它是扩展的 ASCII。它严格来说是每个字符一个字节,并且需要一个排序规则来处理一组有限的外来字符。n(var)char,即UCS-2(与 非常相似UTF-16)。它严格是每个字符两个字节,并且将以双倍内存大小的代价对(几乎)任何已知字符进行编码。UTF-8与 兼容(var)char,只要我们坚持使用纯拉丁语和一字节代码。但是任何高于 127 的 ASCII 代码都会导致麻烦(可能会使用正确的排序规则)。但是 - 这是您的情况 - 您的字符串使用 multi-byte-code-points。UTF-8将为一个字符使用两个或什至更多字节(最多 4 个!)来编码大量字符。
你必须使用一些能够处理 UTF-8 的引擎
\n并且 - 感谢@GSerg - 还有两个选项:
\n\n数据库可以按原样保存存储数据或工作数据,您想以一种或另一种方式使用。将图片存储为VARBINARY(MAX)只是一大块位。您不会尝试使用 SQL-Server 来执行图像识别。
这与文本数据相同。如果您只存储一大块文本,那么如何执行此操作并不重要。但如果你想使用这个文本进行过滤、搜索或者如果你想使用SQL-Server来显示这个文本,你必须考虑格式和性能需求。
\n具有可变字节长度的编码不允许简单的SUBSTRING(\'blahblah\',2,3). 对于固定长度,引擎可以将字符串作为数组,跳转到第二个索引并选择接下来的三个字符。但是对于可变字节,如果可能存在任何多字节代码点,引擎将必须通过检查之前的所有字符来计算索引。这会极大地减慢很多字符串方法的速度......
最好的是,不要以 SQL-Server 无法(很好)处理的格式存储数据......
\nXML 技巧工作正常,只需让 XML 引擎处理字符实体:
declare @t table ([body] nvarchar(max));
insert into @t(body)
values ('REFMTEFTLCBUWCDigJMgTWF5IDcsIDIwMTkg4oCTIENvdmV5ICYgUGFyayBFbmVyZ3kgSG9sZGluZ3MgTExDICjigJxDb3ZleSBQYXJr4oCdIA==');
select
cast(
cast('<?xml version="1.0" encoding="UTF-8"?><root><![CDATA[' as varbinary(max))
+
CAST('' as xml).value('xs:base64Binary(sql:column("body"))', 'VARBINARY(MAX)')
+
cast(']]></root>' as varbinary(max))
as xml).value('.', 'nvarchar(max)')
from
@t;
Run Code Online (Sandbox Code Playgroud)
这里的重要部分是:
N在字符串文字的前面encoding="UTF-8"varbinary为有效的 UTF-8<![CDATA]]>块。请注意,它仍然只是一个黑客攻击。一旦涉及 XML,就会受到 XML 的限制,并且如果您的字符串包含无法在 XML 中表示的字符,则该类型的 XML 转换将失败
XML 解析:第 1 行,字符 54,非法 xml 字符
| 归档时间: |
|
| 查看次数: |
8012 次 |
| 最近记录: |