dbe*_*lev 3 xml t-sql sql-server
我需要生成符合第三方规范的 XML 文件。我遇到麻烦的一种情况是存在 NULL 列。规范规定,除了该xsi:nil="true"属性之外,还需要有一个原因属性。
该节点需要如下所示:
<Phone xsi:nil="true" Reason="none" />
Run Code Online (Sandbox Code Playgroud)
我可以生成具有属性和值的节点,但不能xsi:nil生成具有附加属性的节点。
这将为除属性之外的所有内容生成正确的格式xsi:nil:
DECLARE @T TABLE(
[Id] [int],
[OwnerId] [int],
[Number] [char](12),
[Type] [char](4),
[Reason] [char](4))
INSERT INTO @T VALUES
(1,1,'414-555-1212','cell',NULL),
(2,2,NULL,NULL,'None'),
(3,3,'202-555-1212','work',NULL),
(4,3,'212-555-1212','cell',NULL)
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [Root!1],
NULL AS [Phone!2!OwnerId],
NULL AS [Phone!2!Type],
NULL AS [Phone!2!Reason],
NULL AS [Phone!2]
union all
SELECT
2 AS Tag,
1 AS Parent,
NULL,
OwnerId,
Type,
Reason,
Number
FROM
@T
FOR XML EXPLICIT;
Run Code Online (Sandbox Code Playgroud)
输出是:
<Root>
<Phone OwnerId="1" Type="cell">414-555-1212</Phone>
<Phone OwnerId="2" Reason="None" />
<Phone OwnerId="3" Type="work">202-555-1212</Phone>
<Phone OwnerId="3" Type="cell">212-555-1212</Phone>
</Root>
Run Code Online (Sandbox Code Playgroud)
我可以使用 ELEMENTXSINIL 通过根节点中定义的命名空间来正确处理 NULL 值,但电话号码是不同的元素:
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [Root!1],
NULL AS [Phone!2!OwnerId],
NULL AS [Phone!2!Type],
NULL AS [Phone!2!Reason],
NULL AS [Phone!2!Number!ELEMENTXSINIL]
union all
SELECT
2 AS Tag,
1 AS Parent,
NULL,
OwnerId,
Type,
Reason,
Number
FROM
@T
FOR XML EXPLICIT;
Run Code Online (Sandbox Code Playgroud)
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Phone OwnerId="1" Type="cell">
<Number>414-555-1212</Number>
</Phone>
<Phone OwnerId="2" Reason="None">
<Number xsi:nil="true" />
</Phone>
<Phone OwnerId="3" Type="work">
<Number>202-555-1212</Number>
</Phone>
<Phone OwnerId="3" Type="cell">
<Number>212-555-1212</Number>
</Phone>
</Root>
Run Code Online (Sandbox Code Playgroud)
规范非常一致,并且Reason作为属性仅出现在 NULL 值上。我可以进行一些后处理并添加xsi:nil="true"到根元素中的这些节点和命名空间定义,但我更希望正确执行此步骤。
是否可以?
如果您致力于使用FOR XML EXPLICIT,这并不能真正实现可理解和可维护的代码,那么您可以向Root元素添加命名空间声明并手动添加xsi:nil="true"属性,如下所示:
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [Root!1],
'http://www.w3.org/2001/XMLSchema-instance' AS [Root!1!xmlns:xsi],
NULL AS [Phone!2!OwnerId],
NULL AS [Phone!2!Type],
NULL AS [Phone!2!Reason],
NULL AS [Phone!2!xsi:nil],
NULL AS [Phone!2]
union all
SELECT
2 AS Tag,
1 AS Parent,
NULL,
NULL,
OwnerId,
Type,
Reason,
CASE WHEN Number IS NULL THEN 'true' END,
Number
FROM
@T
FOR XML EXPLICIT;
Run Code Online (Sandbox Code Playgroud)
产生输出:
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Phone OwnerId="1" Type="cell">414-555-1212</Phone>
<Phone OwnerId="2" Reason="None" xsi:nil="true"/>
<Phone OwnerId="3" Type="work">202-555-1212</Phone>
<Phone OwnerId="3" Type="cell">212-555-1212</Phone>
</Root>
Run Code Online (Sandbox Code Playgroud)
可以使用相同的手动属性管理来生成相同的输出,FOR XML PATH如下所示:
WITH XMLNAMESPACES (
'http://www.w3.org/2001/XMLSchema-instance' AS xsi
)
SELECT
OwnerId AS [@OwnerId],
Type AS [@Type],
Reason AS [@Reason],
CASE WHEN Number IS NULL THEN 'true' END AS [@xsi:nil],
Number AS [data()] -- or [node()] or [text()]
FROM @T
FOR XML PATH ('Phone'), ROOT('Root');
Run Code Online (Sandbox Code Playgroud)
ELEMENTS XSINIL尽管您可能期望由于某种原因可以简化上述内容,但FOR XML PATH无法识别 的null输出data(),node()也text()无法添加xsi:nil="true"属性本身。
FOR XML PATH为了更简单地使用,ELEMENTS XSINIL我们可以依靠不同的技术 - 空元素名称从输出中排除:
SELECT
OwnerId AS [Phone/@OwnerId],
Type AS [Phone/@Type],
Reason AS [Phone/@Reason],
Number AS [Phone]
FROM @T
FOR XML PATH (''), ROOT('Root'), ELEMENTS XSINIL;
Run Code Online (Sandbox Code Playgroud)