【问题标题】:How can I merge several XML-documents into one without namespace accumulation?如何在没有命名空间积累的情况下将多个 XML 文档合并为一个?
【发布时间】:2017-12-11 17:47:38
【问题描述】:

我在存储为 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=..." 上进行搜索和替换?

【问题讨论】:

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


【解决方案1】:

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

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

短距离

...如果您真的不需要其他任何东西,除了第一个根目录中的命名空间&lt;svg&gt;...

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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多