如何将xmlns:*属性与SQL匹配?

时间:2018-08-02 17:51:49

标签: sql-server xml xml-namespaces sql-server-2017

问题

有什么方法可以使用在SQL Server的名称空间轴上匹配的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 

研究

NB:有一个similar question询问在XSLT中是如何完成的;但是SQL-Server不包括namespace::轴。 here中提供了SQL中可用轴的列表。

还有其他方法可以消除这种膨胀;但是这些都不是简单明了的,而且这些帖子现在已经过时了,因此我正在研究替代方法:

当前解决方案

注意:由于我一直无法找到简单的解决方案,因此,当前删除这些名称空间的方法如下。这样做有一些风险(例如,丢失根元素的属性,如果缺少预期的空格会出现问题,或者使用其他的空白字符),但这对于我的目的已经足够了,如果它很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 

1 个答案:

答案 0 :(得分:1)

SQL Server处理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 |
+----+----------+----------+-----------+--------+--------------+----------+------+------+

此表包含在递归CTE中动态创建上述语句并使用EXEC从头开始创建XML所需的所有信息。

使用WHERE nodetype=1获得元素,使用2获得属性...

但是-老实说-这是一项巨大的努力...

如果您的XML更复杂,嵌套得多,这将变得非常糟糕...