【问题标题】:How can I match the xmlns:* attributes with SQL?如何将 xmlns:* 属性与 SQL 匹配?
【发布时间】:2019-01-10 13:49:14
【问题描述】:

问题

有什么方法可以使用与 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 

研究

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

还有其他方法可以消除这种膨胀;但没有一个是那么直截了当,而且这些帖子现在已经过时了,因此我研究了替代方法:

当前解决方案

注意:由于我无法找到直接的解决方案,我目前删除这些命名空间的方法如下。这有一些风险(例如,丢失根元素上的属性,如果缺少预期的空格/使用其他空白字符会出现问题),但这对于我的目的来说已经足够了,如果非常hacky & non-generic。

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 

【问题讨论】:

  • 好问题,说得好,太糟糕了,答案似乎确实是“你不能”。引擎真的希望命名空间保持不变并且仅可用作元数据,并且完全删除它们的视图似乎只是实现这一目标的另一种方法。命名空间节点对于 T-SQL 中支持的所有 XPath/XQuery 构造实际上是不可见的。 (这将使删除它们实际上成为可能,通过使用 XQuery 复制其余部分 - 如果 SQL Server 中的 XQuery 引擎支持声明函数,但它不支持。)如果您需要完全可靠,我认为 CLR 函数是唯一的方法去。
  • 为了完整起见,我会把它放在那里,尽管它实际上并没有帮助这项工作:那个老战马OPENXML可以访问命名空间节点,所以你可以这样做(例如)DECLARE @hdoc int; EXEC sp_xml_preparedocument @hdoc OUTPUT, @demo; SELECT * FROM OPENXML(@hdoc, '//*') WHERE prefix &lt;&gt; 'xmlns' or parentid = 0 EXEC sp_xml_removedocument @hdoc; 但是,正如我所说,再次将其转回 XML 要么是不可能的,要么(如果你接受游标)非常非常不方便/效率低下。

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


【解决方案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 在 cmets 中指出的那样,您可能会使用过时的 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 更复杂、更嵌套,无论如何,这将变得非常糟糕......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 2018-05-07
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多