如何在 FOR XML PATH 中将属性放在根元素上,并且只放在根元素上?

How to put an attribute on the root element, and only the root element, in FOR XML PATH?

我正在从 SQL 服务器 table 生成 XML。

这是我的代码:

;WITH XMLNAMESPACES
( 
     'http://www.w3.org/2001/XMLSchema-instance' AS xsi 
    --,DEFAULT 'http://www.w3.org/2001/XMLSchema-instance'  -- xmlns 
) 
SELECT 
    'T_Contracts' AS "@tableName",
    (SELECT * FROM T_Contracts 
     FOR XML PATH('row'), TYPE, ELEMENTS xsinil)
FOR XML PATH('table'), TYPE, ELEMENTS xsinil

我希望结果看起来像这样(注意:根元素上的属性 tableName):

<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" tableName="T_Contracts">
  <row>
    <VTR_UID>779FE899-4E81-4D8C-BF9B-3F17BC1DF146</VTR_UID>
    <VTR_MDT_ID>0</VTR_MDT_ID>
    <VTR_VTP_UID xsi:nil="true" />
    <VTR_Nr>0050/132251</VTR_Nr>
  </row>
</table>

但它在行元素上复制了 XSI 命名空间...

<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" tableName="T_Contracts">
  <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <VTR_UID>779FE899-4E81-4D8C-BF9B-3F17BC1DF146</VTR_UID>
    <VTR_MDT_ID>0</VTR_MDT_ID>
    <VTR_VTP_UID xsi:nil="true" />
    <VTR_Nr>0050/132251</VTR_Nr>
  </row>
</table>

向根元素添加属性 并且仅向根元素 添加属性的正确方法是什么?

备注

NULL 值必须作为 <columnName xsi:nil="true" /> 返回并且不能被省略。

(在 select 之后没有 xml.modify

请注意,这不是现有问题的重复。

为什么不手动构建根元素?

示例:

with CTE as (
  select (select * from T_Contracts for xml path('row')) as MyXML
)
select '<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" tableName="T_Contracts">' +
        MyXML + 
        '</table>'  
from CTE

这种带有子查询的重复命名空间的恼人行为是 MS-Connect 上 10 多年来的一个报告问题,获得了数千票。这个平台被驳回了,这个问题也被驳回了,微软永远不会解决这个问题。

公平地说:重复命名空间声明并没有错。它只是膨胀了基于字符串的输出...

更奇怪的是根级节点上不受支持的属性...

好吧,如果你需要头痛,你可以看看 OPTION EXPLICIT :-)

Marc Guillot 接受的答案不会产生您似乎需要的 xsi:nil="true" 属性。它只会用适当的根节点包装您的结果。

最后:用XML方法解决不了,可以试试这个:
更新:找到方法,见下文...

DECLARE @tbl TABLE(ID INT,SomeValue INT);
INSERT INTO @tbl VALUES(1,1),(2,NULL);

SELECT CAST(REPLACE(CAST(
(
    SELECT *
    FROM @tbl 
    FOR XML PATH('row'),ROOT('table'),TYPE, ELEMENTS XSINIL
) AS nvarchar(MAX)),'<table ','<table tableName="T_Contracts" ') AS XML);

结果

<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" tableName="T_Contracts">
  <row>
    <ID>1</ID>
    <SomeValue>1</SomeValue>
  </row>
  <row>
    <ID>2</ID>
    <SomeValue xsi:nil="true" />
  </row>
</table>

简而言之:

  • 我们在没有子查询的情况下创建 XML 并使用字符串方法将属性添加到已转换的 XML.
  • 由于属性的位置并不重要,我们可以在任何地方添加它。
  • 或者您可以搜索第一个收盘价 > 并在那里使用 STUFF()...

更新

嘿,我刚刚找到了一种方法,可以在不切换到字符串的情况下创建它,但它很笨拙:-)

DECLARE @tbl TABLE(ID INT,SomeValue INT);
INSERT INTO @tbl VALUES(1,1),(2,NULL);

SELECT
(
SELECT 'T_Contracts' AS [@tableName]
      ,(
        SELECT 'SomeRowAttr' AS [@testAttr] --added this to test row-level attributes
              ,*
        FROM @tbl 
        FOR XML PATH('row'),TYPE, ELEMENTS XSINIL
       )
FOR XML PATH('table'),TYPE, ELEMENTS XSINIL
).query('<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">{/table/@*}
         {
            for $nd in /table/row
            return
            <row>{$nd/@*}
            {
                $nd/*
            }
            </row>
         }
         </table>');

结果

<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" tableName="T_Contracts">
  <row testAttr="SomeRowAttr">
    <ID>1</ID>
    <SomeValue>1</SomeValue>
  </row>
  <row testAttr="SomeRowAttr">
    <ID>2</ID>
    <SomeValue xsi:nil="true" />
  </row>
</table>

不幸的是,您不能使用开箱即用的 SQL 服务器来执行此操作,也不存在执行此操作的优雅方法。要缓解此问题,您可以将 NULLs 替换为空字符串。这将删除 xmlns,但您必须按如下方式明确定义 select 列表。此外,这仅适用于字符串数据类型,因为您不能将空字符串(ISNULL 函数中的 '')分配给 - 例如 - 一个整数。

;WITH XMLNAMESPACES
( 
     'http://www.w3.org/2001/XMLSchema-instance' AS xsi 
    --,DEFAULT 'http://www.w3.org/2001/XMLSchema-instance'  -- xmlns 
) 
SELECT 'T_Contracts' AS "@tableName",
    (
        SELECT 
             ISNULL(VTR_UID, '')     'row/VTR_UID'
            ,ISNULL(VTR_MDT_ID, '')  'row/VTR_MDT_ID'
            ,ISNULL(VTR_VTP_UID, '') 'row/VTR_VTP_UID'
            ,ISNULL(VTR_Nr, '')      'row/VTR_Nr'
        FROM T_Contracts
        FOR XML PATH(''), TYPE
    )
FOR XML PATH('table'), TYPE, ELEMENTS xsinil

结果如下:

<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" tableName="T_Contracts">
  <row>
    <VTR_UID>779FE899-4E81-4D8C-BF9B-3F17BC1DF146</VTR_UID>
    <VTR_MDT_ID>0</VTR_MDT_ID>
    <VTR_VTP_UID />
    <VTR_Nr>0050/132251</VTR_Nr>
  </row>
</table>