如何在 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 服务器来执行此操作,也不存在执行此操作的优雅方法。要缓解此问题,您可以将 NULL
s 替换为空字符串。这将删除 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>
我正在从 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 服务器来执行此操作,也不存在执行此操作的优雅方法。要缓解此问题,您可以将 NULL
s 替换为空字符串。这将删除 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>