转换时转义XML特殊字符

时间:2018-12-07 21:46:47

标签: sql xml csv tsql 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

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

insert into @t select 'i,j,&,k'

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

一种解决方案是将字符替换为&amp ,就像这样:

select convert(xml,'<f><e>' + replace(replace(data,'&','&amp'),',', '</e><e>') + '</e></f>') as xm 

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

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

谢谢!

1 个答案:

答案 0 :(得分:2)

您已经得到了马丁·史密斯的答案。但是我认为,在这里为追随者提供答案是值得的。要提供一些解释和进一步的信息,rextester链接将来可能无法访问...

如果您想到这样的表中的字符串...

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

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

SELECT '<root>' + SomeXMLstring + '</root>'
FROM @mockup ;

-结果看起来像XML

<root>This is a string with forbidden characters like "<", ">" or "&"</root>

-但事实并非如此!您可以对此进行测试,CAST( AS XML)将失败:

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

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

-但是XML隐含地为我们做这一切

SELECT SomeXMLstring 
FROM @mockup
FOR XML PATH('')

-这是结果

<SomeXMLstring>This is a string with forbidden characters like "&lt;", "&gt;" or "&amp;"</SomeXMLstring>

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

SELECT SomeXMLstring AS [*]
FROM @mockup
FOR XML PATH('')

-结果相同,但没有标签:

This is a string with forbidden characters like "&lt;", "&gt;" or "&amp;"

-尽管在SSMS中看起来像XML,但是当用作字符串时,它将隐式转换为NVARCHAR(MAX)

-您可以在需要构建带有字符串连接的XML的任何地方使用它来隐式转义字符串:

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

最终回答您的问题

此行必须使用技巧:

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