转换时转义 XML 特殊字符

Avi*_*hol 2 xml sql t-sql csv sql-server-2014

我有可以满足我需要的csv分割器。

您可以按原样获取并运行它:

declare @t table(data varchar(max))
insert into @t select 'a,b,c,d'
insert into @t select 'e,,,h'

;with cte(xm) as 
(
    select convert(xml,'<f><e>' + replace(data,',', '</e><e>') + '</e></f>') as xm 
    from @t
)
select
    xm.value('/f[1]/e[1]','varchar(32)'),
    xm.value('/f[1]/e[2]','varchar(32)'),
    xm.value('/f[1]/e[3]','varchar(32)'),
    xm.value('/f[1]/e[4]','varchar(32)')
from cte
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,如果我在data中引入 XML 敏感字符,例如&

insert into @t select 'i,j,&,k'
Run Code Online (Sandbox Code Playgroud)

它失败并出现错误:字符 24,非法字符

一种解决方案是将&字符即时替换为& ,如下所示:

select convert(xml,'<f><e>' + replace(replace(data,'&','&amp'),',', '</e><e>') + '</e></f>') as xm 
Run Code Online (Sandbox Code Playgroud)

但是有几十个特殊的 XML 字符我需要在转换时转义,而且我不能真正在其中嵌套几十个 Replace (replace(replace(...函数)。这就是我所做的,而且很混乱。

如何修改上面的代码以转义 XML 敏感字符并产生相同的结果?

谢谢!

Shn*_*ugo 12

马丁·史密斯已经给你答案了。但我认为,值得为追随者在这里提供一个答案。想要提供一些解释,此外,reextester 链接将来可能无法访问......

如果你想像这样的表中的字符串......

DECLARE @mockup TABLE(SomeXMLstring VARCHAR(100));
INSERT INTO @mockup VALUES('This is a string with forbidden characters like "<", ">" or "&"');
Run Code Online (Sandbox Code Playgroud)

-- ...您可以轻松添加 XML 标签:

SELECT '<root>' + SomeXMLstring + '</root>'
FROM @mockup ;
Run Code Online (Sandbox Code Playgroud)

--结果看起来像 XML

<root>This is a string with forbidden characters like "<", ">" or "&"</root>
Run Code Online (Sandbox Code Playgroud)

——但事实并非如此!你可以测试一下,CAST( AS XML)会失败:

SELECT CAST('<root>This is a string with forbidden characters like "<", ">" or "&"</root>' AS XML);
Run Code Online (Sandbox Code Playgroud)

-有时人们尝试进行自己的替换并开始<, > and &用相应的实体进行替换&lt;, &gt; and &amp;。但这需要大量更换才能安全

--但是 XML 正在隐式地为我们做这一切

SELECT SomeXMLstring 
FROM @mockup
FOR XML PATH('')
Run Code Online (Sandbox Code Playgroud)

--这就是结果

<SomeXMLstring>This is a string with forbidden characters like "&lt;", "&gt;" or "&amp;"</SomeXMLstring>
Run Code Online (Sandbox Code Playgroud)

--有趣的是:我们可以轻松地创建一个无名元素AS [*]

SELECT SomeXMLstring AS [*]
FROM @mockup
FOR XML PATH('')
Run Code Online (Sandbox Code Playgroud)

--结果是一样的,但没有标签:

This is a string with forbidden characters like "&lt;", "&gt;" or "&amp;"
Run Code Online (Sandbox Code Playgroud)

--虽然这看起来像 SSMS 中的 XML,但NVARCHAR(MAX)在用作字符串时将被隐式转换为。

--只要您觉得需要使用字符串连接构建 XML,您就可以使用它来隐式转义字符串:

SELECT CAST('<root>' + (SELECT SomeXMLstring AS [*] FOR XML PATH('')) + '</root>' AS XML)
FROM @mockup ;
Run Code Online (Sandbox Code Playgroud)

最后回答你的问题

这一行必须使用技巧:

select convert(xml,'<f><e>' + replace((SELECT data AS [*] FOR XML PATH('')),',', '</e><e>') + '</e></f>') as xm
Run Code Online (Sandbox Code Playgroud)