sql - 如何在没有 namespace 积累的情况下将多个 XML 文档合并为一个文档?

标签 sql sql-server xml tsql xml-namespaces

我在存储为 xml 文档的表中有几个 SVG 段。 现在我需要从该表中选择所有元素,并将它们合并到一个 XML 文档中。

这是我的 T-SQL 代码:

declare @xml table (xmldocument xml)
insert @xml select '
<svg xmlns="http://www.w3.org/2000/svg" otherattrib="x">
  <path id="789" data-objid="0000X2"></path>
</svg>'

insert @xml select '
<svg xmlns="http://www.w3.org/2000/svg" otherattrib="x">
  <admin>
  <g>
  <path></path>
  <path data-objid="0000X1"></path>
  <path id="123" data-objid="0000X2"></path>
  <path id="456" data-objid="0000X3"></path>
  </g>
  </admin>

    <g>
    <path></path>
    <path data-objid="0000X1"></path>
  <path id="789" data-objid="0000X2"></path>
  <path id="abc" data-objid="0000X3"></path>
  </g>
</svg>'


insert @xml select '
<svg xmlns="http://www.w3.org/2000/svg" otherattrib="x">
    <path></path>
</svg>'


insert @xml select '
<svg xmlns="http://www.w3.org/2000/svg" otherattrib="x">
  <path id="abc" data-objid="0000X3"></path>
</svg>'




--;WITH XMLNAMESPACES ('http://www.w3.org/2000/svg' AS svg)
;WITH XMLNAMESPACES (default 'http://www.w3.org/2000/svg')
--SELECT 
    --(
        SELECT 
             --xmldocument 

            --,c.p.value('.', 'nvarchar(MAX)')
            c.p.query('declare default element namespace "http://www.w3.org/2000/svg";.') 
        FROM @xml AS t

        OUTER APPLY t.xmldocument.nodes('/svg//*') AS c(p)

        FOR XML PATH(''), root('svg')
--  ) AS merged

但这会产生

<svg xmlns="http://www.w3.org/2000/svg">
  <path xmlns="http://www.w3.org/2000/svg" id="789" data-objid="0000X2" />
  <admin xmlns="http://www.w3.org/2000/svg">
    <g>
      <path />
      <path data-objid="0000X1" />
      <path id="123" data-objid="0000X2" />
      <path id="456" data-objid="0000X3" />
    </g>
  </admin>
  <g xmlns="http://www.w3.org/2000/svg">
    <path />
    <path data-objid="0000X1" />
    <path id="123" data-objid="0000X2" />
    <path id="456" data-objid="0000X3" />
  </g>
  <path xmlns="http://www.w3.org/2000/svg" />
  <path xmlns="http://www.w3.org/2000/svg" data-objid="0000X1" />
  <path xmlns="http://www.w3.org/2000/svg" id="123" data-objid="0000X2" />
  <path xmlns="http://www.w3.org/2000/svg" id="456" data-objid="0000X3" />
  <g xmlns="http://www.w3.org/2000/svg">
    <path />
    <path data-objid="0000X1" />
    <path id="789" data-objid="0000X2" />
    <path id="abc" data-objid="0000X3" />
  </g>
  <path xmlns="http://www.w3.org/2000/svg" />
  <path xmlns="http://www.w3.org/2000/svg" data-objid="0000X1" />
  <path xmlns="http://www.w3.org/2000/svg" id="789" data-objid="0000X2" />
  <path xmlns="http://www.w3.org/2000/svg" id="abc" data-objid="0000X3" />
  <path xmlns="http://www.w3.org/2000/svg" />
  <path xmlns="http://www.w3.org/2000/svg" id="abc" data-objid="0000X3" />
</svg>

代替

<svg xmlns="http://www.w3.org/2000/svg">
  <path id="789" data-objid="0000X2" />
  <admin>
    <g>
      <path />
      <path data-objid="0000X1" />
      <path id="123" data-objid="0000X2" />
      <path id="456" data-objid="0000X3" />
    </g>
  </admin>
  <g>
    <path />
    <path data-objid="0000X1" />
    <path id="123" data-objid="0000X2" />
    <path id="456" data-objid="0000X3" />
  </g>
  <path />
  <path data-objid="0000X1" />
  <path id="123" data-objid="0000X2" />
  <path id="456" data-objid="0000X3" />
  <g>
    <path />
    <path data-objid="0000X1" />
    <path id="789" data-objid="0000X2" />
    <path id="abc" data-objid="0000X3" />
  </g>
  <path />
  <path data-objid="0000X1" />
  <path id="789" data-objid="0000X2" />
  <path id="abc" data-objid="0000X3" />
  <path />
  <path id="abc" data-objid="0000X3" />
</svg>

我错过了什么?我做错了什么?
如何在不必强制转换为 varchar 的情况下更正此问题,然后在 "xmlns=..."上进行搜索和替换?

最佳答案

首先:重复的命名空间在任何方面都没有错,只是烦人和膨胀了结果的大小......

不幸的是,没有办法以干净的方式摆脱它们。如果您真的需要这个,您将转换为文本并在字符串级别执行此操作的想法还不错(但请注意,转换必须转到 NVARCHAR 并且重新转换为 XML 可能会改变您的 XML 结构(属性顺序,CDATA -sections...)。试试这个:

短方法

...如果您真的不需要任何其他东西,除了第一个根 <svg> 中的 namespace ...

DECLARE @NewXML NVARCHAR(MAX)=
(
    SELECT t.xmldocument.query('declare default element namespace "http://www.w3.org/2000/svg";svg/*')
    FROM @xml AS t
    FOR XML PATH('')
);
SELECT CAST(N'<svg xmlns="http://www.w3.org/2000/svg">' 
          + REPLACE(@NewXML,' xmlns="http://www.w3.org/2000/svg"','') 
          + N'</svg>' AS XML);

更灵活的方法

你可以试试这个:

DECLARE @NewXML XML=
(
    SELECT t.xmldocument.query('declare default element namespace "http://www.w3.org/2000/svg";svg/*')
    FROM @xml AS t
    FOR XML PATH(''),TYPE
);
SET @NewXML =CAST(REPLACE(CAST(@NewXML AS NVARCHAR(MAX)),' xmlns="http://www.w3.org/2000/svg"','') AS XML);

--你需要重复 CASTREPLACE ,否则你会得到很多 xmlns="" 为内部节点定义一个空的默认命名空间,这是错误的......

WITH XMLNAMESPACES (DEFAULT 'http://www.w3.org/2000/svg')
SELECT @NewXML=CAST(REPLACE(CAST((SELECT @NewXML FOR XML PATH('svg'),TYPE) AS NVARCHAR(MAX)),' xmlns=""','') AS XML);
SELECT @NewXML;

您可以使用 STUFF() 在字符串级别引入命名空间:

(不要在这里使用WITH XMLNAMESPACES...

SELECT @NewXML=CAST(STUFF(CAST((SELECT @NewXML FOR XML PATH('svg'),TYPE) AS NVARCHAR(MAX)),5,0,' xmlns="http://www.w3.org/2000/svg" ') AS XML);
SELECT @NewXML;

所有情况下的结果

<svg xmlns="http://www.w3.org/2000/svg">
  <path id="789" data-objid="0000X2" />
  <admin>
    <g>
      <path />
      <path data-objid="0000X1" />
      <path id="123" data-objid="0000X2" />
      <path id="456" data-objid="0000X3" />
    </g>
  </admin>
  <g>
    <path />
    <path data-objid="0000X1" />
    <path id="789" data-objid="0000X2" />
    <path id="abc" data-objid="0000X3" />
  </g>
  <path />
  <path id="abc" data-objid="0000X3" />
</svg>

关于sql - 如何在没有 namespace 积累的情况下将多个 XML 文档合并为一个文档?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44970598/

相关文章:

c# - "Why can' t 我编辑这个文件?这个项目需要一个 Xamarin 业务......”当打开某些与 Xamarin 无关的 XML 时

sql - 使用 VBA 在 SQL 中插入德语日期格式

java - SQL 查询 1 :n relation, 查找具有两个匹配子项的所有实体

java - 如何在 grails 独立应用程序中嵌入 mysql

SQL查询按年按月统计事件

sql - 如果前一个值为空,如何获取倒数第二个值

c# - 如何根据 rowversion/timestamp 值查询 Code First 实体?

xml - 是否可以在 SuiteScript 2.0 中将图像从图像字段加载到 PDF 中

java - log4j:如何在控制台中仅记录来自 com.foo.* 的消息?

sql - 选择特定列中具有最大值的行,SQL Server