如何将 xmlns:* 属性与 SQL 匹配?
How can I match the xmlns:* attributes with SQL?
问题
有没有什么方法可以在 SQL 服务器中使用与命名空间轴匹配的 XPath?即我知道 SQL 本身不支持这个轴;但是是否有任何功能相似且可能有效的查询?
上下文
我希望编写代码来减少我的 XML 中的重复命名空间,只留下那些存在于根元素上的声明。我已经看到了其他各种解决方案,但都非常痛苦;所以我研究了替代解决方案,并在这样做时意识到 SQL 不支持命名空间轴。
declare @demo xml = '
<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c xmlns:world="me">demo</world:c>
<hello:d xmlns:hello="test">demo</hello:d>
<world:e xmlns:hello="test" xmlns:world="me">demo</world:e>
<hello:f xmlns:hello="test" xmlns:world="me" world:demo=''x''>demo</hello:f>
</hello:b>
</hello:a>
'
set @demo.modify('delete (/*//namespace::*)')
--set @demo.modify('delete (/*//@*[not(namespace-uri() > "")])') --tried just in case xmlns is treated as an attribute in SQL; no joy :/
select @demo
研究
注意:有一个 similar question asking how this is done in XSLT; but SQL-Server does not include the namespace::
axis. A list of the available axes in SQL is available here。
还有其他方法可以消除这种膨胀;但是 none 就是这么简单,而且这些帖子现在已经过时了,因此我正在研究替代方法:
- 对各种可用方法的很好的概述:https://social.msdn.microsoft.com/Forums/sqlserver/en-US/2f7bdfbf-8e40-456b-84e8-195318649703/how-to-remove-namespaces-from-xml-tags-when-using-for-xml-option-with-xmlnamespaces?forum=transactsql&prof=required
- 有关在 SQL How do I remove redundant namespace in nested query when using FOR XML PATH
中生成 XML 时如何避免此问题的一些答案
当前解决方案
注意:由于我一直无法找到直接的解决方案,我目前删除这些命名空间的方法如下。这有一些风险(例如,丢失根元素的属性,如果缺少预期的空格/使用其他空白字符会出现问题),但这对于我的目的来说已经足够了,如果非常 hacky 和非通用的话。
declare @demo xml = '
<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c xmlns:world="me">demo</world:c>
<hello:d xmlns:hello="test">demo</hello:d>
<world:e xmlns:hello="test" xmlns:world="me">demo</world:e>
<hello:f xmlns:hello="test" xmlns:world="me" world:demo=''x''>demo</hello:f>
</hello:b>
</hello:a>
'
;with xmlnamespaces('test' as hello, 'me' as world)
select @demo = cast(
'<hello:a xmlns:hello="test" xmlns:world="me">'
+ replace(
replace(
cast(@demo.query('/*/*') as nvarchar(max))
,' xmlns:hello="test"'
,''
)
,' xmlns:world="me"'
,''
)
+ '</hello:a>'
as xml
)
select @demo
SQL-服务器处理 XML 命名空间的能力真的 - 嗯 - 痛苦...
我所知道的按照您的意愿定义命名空间的唯一方法是 FOR XML EXPLICIT
(除非您想走 string-manipulation 路线...)
您可以创建您想要的 XML:
SELECT 1 AS Tag
,NULL AS Parent
,'test' AS [hello:a!1!xmlns:hello]
,'me' AS [hello:a!1!xmlns:world]
,NULL AS [hello:b!2]
,NULL AS [world:c!3]
,NULL AS [hello:d!4]
,NULL AS [world:e!5]
,NULL AS [hello:f!6]
,NULL AS [hello:f!6!world:demo]
UNION ALL
SELECT 2
,1
,NULL
,NULL
,''
,NULL
,NULL
,NULL
,NULL
,NULL
UNION ALL
SELECT 3
,2
,NULL
,NULL
,''
,'demo'
,NULL
,NULL
,NULL
,NULL
UNION ALL
SELECT 4
,2
,NULL
,NULL
,''
,NULL
,'demo'
,NULL
,NULL
,NULL
UNION ALL
SELECT 5
,2
,NULL
,NULL
,''
,NULL
,NULL
,'demo'
,NULL
,NULL
UNION ALL
SELECT 6
,2
,NULL
,NULL
,''
,NULL
,NULL
,NULL
,'demo'
,'x'
FOR XML EXPLICIT;
结果
<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c>demo</world:c>
<hello:d>demo</hello:d>
<world:e>demo</world:e>
<hello:f world:demo="x">demo</hello:f>
</hello:b>
</hello:a>
正如 Jeroen Mostert 在评论中指出的那样,您可能会像这里一样使用过时的 FROM OPEN XML
:
declare @demo xml =
'<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c xmlns:world="me">demo</world:c>
<hello:d xmlns:hello="test">demo</hello:d>
<world:e xmlns:hello="test" xmlns:world="me">demo</world:e>
<hello:f xmlns:hello="test" xmlns:world="me" world:demo=''x''>demo</hello:f>
</hello:b>
</hello:a>';
DECLARE @hdoc int;
EXEC sp_xml_preparedocument @hdoc OUTPUT, @demo;
SELECT *
FROM OPENXML(@hdoc, '//*');
EXEC sp_xml_removedocument @hdoc;
GO
结果
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| id | parentid | nodetype | localname | prefix | namespaceuri | datatype | prev | text |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 0 | NULL | 1 | a | hello | test | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 2 | 0 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 20 | 2 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 3 | 0 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 21 | 3 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 4 | 0 | 1 | b | hello | test | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 5 | 4 | 1 | c | world | me | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 6 | 5 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 22 | 6 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 7 | 5 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 8 | 4 | 1 | d | hello | test | NULL | 5 | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 9 | 8 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 23 | 9 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 10 | 8 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 11 | 4 | 1 | e | world | me | NULL | 8 | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 12 | 11 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 24 | 12 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 13 | 11 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 25 | 13 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 14 | 11 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 15 | 4 | 1 | f | hello | test | NULL | 11 | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 16 | 15 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 26 | 16 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 17 | 15 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 27 | 17 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 18 | 15 | 2 | demo | world | me | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 28 | 18 | 3 | #text | NULL | NULL | NULL | NULL | x |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 19 | 15 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
此 table 包含在递归 CTE 中动态创建上述语句并使用 EXEC
从头开始创建 XML 所需的所有信息。
用 WHERE nodetype=1
得到元素,用 2
得到属性...
但是 - 老实说 - 这是一项巨大的努力...
如果您的 XML 更复杂,嵌套,无论如何,这将变得非常糟糕...
问题
有没有什么方法可以在 SQL 服务器中使用与命名空间轴匹配的 XPath?即我知道 SQL 本身不支持这个轴;但是是否有任何功能相似且可能有效的查询?
上下文
我希望编写代码来减少我的 XML 中的重复命名空间,只留下那些存在于根元素上的声明。我已经看到了其他各种解决方案,但都非常痛苦;所以我研究了替代解决方案,并在这样做时意识到 SQL 不支持命名空间轴。
declare @demo xml = '
<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c xmlns:world="me">demo</world:c>
<hello:d xmlns:hello="test">demo</hello:d>
<world:e xmlns:hello="test" xmlns:world="me">demo</world:e>
<hello:f xmlns:hello="test" xmlns:world="me" world:demo=''x''>demo</hello:f>
</hello:b>
</hello:a>
'
set @demo.modify('delete (/*//namespace::*)')
--set @demo.modify('delete (/*//@*[not(namespace-uri() > "")])') --tried just in case xmlns is treated as an attribute in SQL; no joy :/
select @demo
研究
注意:有一个 similar question asking how this is done in XSLT; but SQL-Server does not include the namespace::
axis. A list of the available axes in SQL is available here。
还有其他方法可以消除这种膨胀;但是 none 就是这么简单,而且这些帖子现在已经过时了,因此我正在研究替代方法:
- 对各种可用方法的很好的概述:https://social.msdn.microsoft.com/Forums/sqlserver/en-US/2f7bdfbf-8e40-456b-84e8-195318649703/how-to-remove-namespaces-from-xml-tags-when-using-for-xml-option-with-xmlnamespaces?forum=transactsql&prof=required
- 有关在 SQL How do I remove redundant namespace in nested query when using FOR XML PATH 中生成 XML 时如何避免此问题的一些答案
当前解决方案
注意:由于我一直无法找到直接的解决方案,我目前删除这些命名空间的方法如下。这有一些风险(例如,丢失根元素的属性,如果缺少预期的空格/使用其他空白字符会出现问题),但这对于我的目的来说已经足够了,如果非常 hacky 和非通用的话。
declare @demo xml = '
<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c xmlns:world="me">demo</world:c>
<hello:d xmlns:hello="test">demo</hello:d>
<world:e xmlns:hello="test" xmlns:world="me">demo</world:e>
<hello:f xmlns:hello="test" xmlns:world="me" world:demo=''x''>demo</hello:f>
</hello:b>
</hello:a>
'
;with xmlnamespaces('test' as hello, 'me' as world)
select @demo = cast(
'<hello:a xmlns:hello="test" xmlns:world="me">'
+ replace(
replace(
cast(@demo.query('/*/*') as nvarchar(max))
,' xmlns:hello="test"'
,''
)
,' xmlns:world="me"'
,''
)
+ '</hello:a>'
as xml
)
select @demo
SQL-服务器处理 XML 命名空间的能力真的 - 嗯 - 痛苦...
我所知道的按照您的意愿定义命名空间的唯一方法是 FOR XML EXPLICIT
(除非您想走 string-manipulation 路线...)
您可以创建您想要的 XML:
SELECT 1 AS Tag
,NULL AS Parent
,'test' AS [hello:a!1!xmlns:hello]
,'me' AS [hello:a!1!xmlns:world]
,NULL AS [hello:b!2]
,NULL AS [world:c!3]
,NULL AS [hello:d!4]
,NULL AS [world:e!5]
,NULL AS [hello:f!6]
,NULL AS [hello:f!6!world:demo]
UNION ALL
SELECT 2
,1
,NULL
,NULL
,''
,NULL
,NULL
,NULL
,NULL
,NULL
UNION ALL
SELECT 3
,2
,NULL
,NULL
,''
,'demo'
,NULL
,NULL
,NULL
,NULL
UNION ALL
SELECT 4
,2
,NULL
,NULL
,''
,NULL
,'demo'
,NULL
,NULL
,NULL
UNION ALL
SELECT 5
,2
,NULL
,NULL
,''
,NULL
,NULL
,'demo'
,NULL
,NULL
UNION ALL
SELECT 6
,2
,NULL
,NULL
,''
,NULL
,NULL
,NULL
,'demo'
,'x'
FOR XML EXPLICIT;
结果
<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c>demo</world:c>
<hello:d>demo</hello:d>
<world:e>demo</world:e>
<hello:f world:demo="x">demo</hello:f>
</hello:b>
</hello:a>
正如 Jeroen Mostert 在评论中指出的那样,您可能会像这里一样使用过时的 FROM OPEN XML
:
declare @demo xml =
'<hello:a xmlns:hello="test" xmlns:world="me">
<hello:b>
<world:c xmlns:world="me">demo</world:c>
<hello:d xmlns:hello="test">demo</hello:d>
<world:e xmlns:hello="test" xmlns:world="me">demo</world:e>
<hello:f xmlns:hello="test" xmlns:world="me" world:demo=''x''>demo</hello:f>
</hello:b>
</hello:a>';
DECLARE @hdoc int;
EXEC sp_xml_preparedocument @hdoc OUTPUT, @demo;
SELECT *
FROM OPENXML(@hdoc, '//*');
EXEC sp_xml_removedocument @hdoc;
GO
结果
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| id | parentid | nodetype | localname | prefix | namespaceuri | datatype | prev | text |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 0 | NULL | 1 | a | hello | test | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 2 | 0 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 20 | 2 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 3 | 0 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 21 | 3 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 4 | 0 | 1 | b | hello | test | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 5 | 4 | 1 | c | world | me | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 6 | 5 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 22 | 6 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 7 | 5 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 8 | 4 | 1 | d | hello | test | NULL | 5 | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 9 | 8 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 23 | 9 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 10 | 8 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 11 | 4 | 1 | e | world | me | NULL | 8 | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 12 | 11 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 24 | 12 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 13 | 11 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 25 | 13 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 14 | 11 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 15 | 4 | 1 | f | hello | test | NULL | 11 | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 16 | 15 | 2 | hello | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 26 | 16 | 3 | #text | NULL | NULL | NULL | NULL | test |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 17 | 15 | 2 | world | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 27 | 17 | 3 | #text | NULL | NULL | NULL | NULL | me |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 18 | 15 | 2 | demo | world | me | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 28 | 18 | 3 | #text | NULL | NULL | NULL | NULL | x |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
| 19 | 15 | 3 | #text | NULL | NULL | NULL | NULL | demo |
+----+----------+----------+-----------+--------+--------------+----------+------+------+
此 table 包含在递归 CTE 中动态创建上述语句并使用 EXEC
从头开始创建 XML 所需的所有信息。
用 WHERE nodetype=1
得到元素,用 2
得到属性...
但是 - 老实说 - 这是一项巨大的努力...
如果您的 XML 更复杂,嵌套,无论如何,这将变得非常糟糕...