Sib*_*ima 5 xml t-sql sql-server json sql-server-2016
我正在尝试使用 SQL2016 中的 FOR JSON PATH 将 XML 列转换为 Json,但遇到一些问题。给定以下 XML(请注意,某些 Product 元素可能包含 Product 列表):
<Request>
<SelectedProducts>
<Product id="D04C01S01" level="1" />
<Product id="158796" level="1" />
<Product id="7464" level="2">
<Product id="115561" level="3" />
</Product>
<Product id="907" level="2">
<Product id="12166" level="3" />
<Product id="33093" level="3" />
<Product id="33094" level="3" />
<Product id="28409" level="3" />
</Product>
<Product id="3123" level="2">
<Product id="38538" level="3" />
<Product id="37221" level="3" />
</Product>
</SelectedProducts>
</Request>
Run Code Online (Sandbox Code Playgroud)
我可以在 SQL 上运行以下语句(其中 @xml 是上面的 XML):
SELECT
d.value('./@id', 'varchar(50)') AS 'Id'
,d.value('./@level', 'int') AS 'Level'
,(SELECT
--f.value('../@id', 'varchar(50)') AS 'ParentId'
f.value('./@id', 'varchar(50)') AS 'Id'
,f.value('./@level', 'int') AS 'Level'
--FROM @xml.nodes('/Request/SelectedProducts/Product[@id="3123"]/Product') AS e(f)
FROM @xml.nodes('/Request/SelectedProducts/Product/Product') AS e(f)
FOR JSON PATH) 'Product'
FROM @xml.nodes('/Request/SelectedProducts/Product') AS c(d)
FOR JSON PATH
Run Code Online (Sandbox Code Playgroud)
它生成的Json是这样的:
[{"Id":"D04C01S01",
"Level":2,
"Product":[{"Id":"115561", "Level":3 }, {"Id":"12166","Level":3 }, { Id":"33093", "Level":3 }, {"Id":"33094","Level":3 }, {"Id":"28409","Level":3},
{"Id":"38538","Level":3},{"Id":"37221","Level":3 }]},
{"Id":"158796",
"Level":3,
"Product":[{"Id":"115561", "Level":3 }, {"Id":"12166","Level":3 }, { Id":"33093", "Level":3 }, {"Id":"33094","Level":3 }, {"Id":"28409","Level":3},
{"Id":"38538","Level":3},{"Id":"37221","Level":3 }]...
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,问题在于,在生成的 Json 中,所有元素最终都会生成所有 Product,无论它们的父关系如何。
我想我缺少一个 WHERE 子句,我可以在其中检查它是否属于父节点,但我不知道如何进行。
我尝试添加节点 Product[@id="3123"] (请参阅注释行),但我需要替换实际父 id 的“3123”,但我不知道该怎么做。
另一种选择是实际保存父 ID(请参阅注释行 ParentId),然后在结果中使用 JSON_MODIFY 删除不匹配的元素,但我也没有成功。
有人对我如何解决这个问题有任何想法吗?或者我还能做什么?
-- 编辑 这是我期待的 Json:
[{"Request":
[{"Id":"D04C01S01","Level":1 },
{"Id":"158796","Level":1},
{"Id":"7464","Level":2,"Product":[{"Id":"115561","Level":3}]},
{"Id":"907","Level":2,"Product":[{"Id":"12166","Level":3},{"Id":"33093","Level":3},{"Id":"33094","Level":3},{"Id":"28409","Level":3}]},
{"Id":"3123","Level":2,"Product":[{"Id":"38538","Level":3},{"Id":"37221","Level":3}]}]}]
Run Code Online (Sandbox Code Playgroud)
您可以假设,如果 Level=1,则不会有产品子级别,如果 Level=2,则将有产品子级别。
谢谢
内部节点集上的 XPath 将从 XML 中选择所有节点,而不仅仅是外部节点的子节点。
(我身上没有 SQL2016 的副本,但类似的东西应该可以工作。)
SELECT
d.value('./@id', 'varchar(50)') AS 'Id'
,d.value('./@level', 'int') AS 'Level'
,(SELECT
f.value('./@id', 'varchar(50)') AS 'Id'
,f.value('./@level', 'int') AS 'Level'
FROM c.d.nodes('./Product') AS e(f)
FOR JSON PATH) 'Product'
FROM @xml.nodes('/Request/SelectedProducts/Product') AS c(d)
FOR JSON PATH
Run Code Online (Sandbox Code Playgroud)