我从this answer那里得到了这个想法,并提出了进一步的问题。我定义了一个变量:
declare @json nvarchar(max)
set @json =
N'{
"Book":{
"IssueDate":"02-15-2019"
, "Detail":{
"Type":"Any Type"
, "Author":{
"Name":"Annie"
, "Sex":"Female"
}
}
, "Chapter":[
{
"Section":"1.1"
, "Title":"Hello world."
}
,
{
"Section":"1.2"
, "Title":"Be happy."
}
]
, "Sponsor":["A","B","C"]
}
}'
然后我执行查询:
select
x.[key] topKey
, y.[key]
, y.[value]
, '{"'+ y.[key] + '":' + y.[value] +'}' jsonString
from openjson(@json) x
cross apply openjson(x.[value]) y
我从表(即@json
)中重置了变量jsonString
,并重复执行上述查询。
以下是执行的结果:
我一直试图将上面的结果存储到一个表中,并创建了下面的函数:
create function ParseJson(
@parent nvarchar(max), @json nvarchar(max))
returns @tempTable table (topKey nvarchar(max), FieldName nvarchar(max), FieldValue nvarchar(max), IsType int)
as
begin
; with cte as (
select
x.[key] topKey,
y.[key] FieldName,
y.[value] FieldValue
, iif([dbo].GetTypeId(y.[Key]) is null or y.[Key] = 'Type' ,0 ,1) IsType
from
openjson(@json) x
cross apply openjson(x.[value]) y
)
insert
@tempTable
select
x.*
from
cte x
union all
select
z.*
from
cte y
cross apply ParseJson(default,'{"'+ y.FieldName + '":' + y.FieldValue+'}') z
where y.IsType=1
return
end
-- execute
select * from ParseJson(default, @json)
IsType
字段是检查是否需要递归的条件。
[dbo].GetTypeId
是用户定义的函数,用于检查FieldValue
是否为最终值,尽管它可能看起来并不适合于此。
以下是函数GetTypeId
和Type
表:
create function GetTypeId(
@typeName nvarchar(255)
)
returns nvarchar(1000)
as
begin
declare
@typeId nvarchar(1000)
select
@typeId=id
from
[Type]
where
[Type].[Name]=@typeName
return @typeId
end
go
这是错误消息:
JSON文本格式不正确。在位置13处发现意外字符'0'。
答案 0 :(得分:2)
对于如何使用mssql递归解析JSON字符串,我相信我对您的链接问题的回答也已经回答了这一问题。
从您提到的关于 type 的线索并查看您的代码,我们可以推断出您的JSON数据是不平凡的,并且存在某种模式。但是,您根本没有描述引擎盖下的机制。
基于我刚才所做的假设,让我们进一步猜测。假设您有一个名为Type
的{{1}}作为非终端值的类型,而类型为SomeType1
作为一种终端值的类型,由于某些东西由其他类型的值组成,因此不太可能解析但直接的符号;而且您必须告诉我们:
1)当我们得到String
和GetTypeId
时,SomeType1
如何区分它们,一个是终端,另一个是非终端?
2)String
在CTE中的工作方式以及属性名称如何与cross apply
相关,因为它们将以GetTypeId
的形式传递?
如果您的JSON数据有一个外部模式,我个人认为代码只是弄乱了东西,尤其是与属性和类型有关的东西;如果没有,那就更糟了..您要的是无价之宝,在这种情况下,恕我直言,无能为力。
答案 1 :(得分:2)
看起来和听起来像您在尝试在表中获得良好的非冗余编码,但还不完全清楚。
如果是这样,这是我用来执行类似操作的查询。查看输出,看看这是否是您想要的。有两点。首先,很容易从isjson()
函数确定终端节点,该终端节点将返回0的值(和null)。其次,构建任意Ids比让json构建自己的ID困难。第三,我输入了一个或两个空值以捕获所有法律条件,最后...我作弊了格式(列为nvarchar(4000)和nvarchar(max)。 ..因此最终选择进行了转换...但是我不想弄混查询)
declare @j nvarchar(max) =
N'{
"Book":{
"IssueDate":"02-15-2019"
, "Detail":{
"Type":"Any Type"
, "Author":{
"Name":"Annie"
, "Sex":"Female"
}
}
, "Chapter":[
{
"Section":"1.1"
, "Title":"Hello world."
}
,
{
"Section":"1.2"
, "Title":"Be happy."
}
]
, "Sponsor":["A","B","C",null]
, "Hooey":null
}
}';
with nodes as
(
select
[key] ParentId,
[key] Id,
[key] Node,
[value] Val,
[type] NodeType,
isnull(abs(isjson([value])-1),1) IsTerminal
from
openjson( @j ) j
union all
select
nodes. Id,
nodes. Id + '.' + j.[key],
j.[key],
j.[value],
j.[type],
isnull(abs(isjson( j.[value] )-1),1)
from
nodes
outer apply
openjson( nodes.Val ) j
where
isjson( nodes.Val ) = 1
)
select
nodes.ParentId,
nodes. Id,
nodes.Node,
case when NodeType= 5 then '{}' when NodeType=4 then '[]' else Val end Val,
nodes.NodeType,
nodes.IsTerminal
from
nodes
...返回:
ParentId Id Node Val NodeType IsTerminal
-------------------- ------------------------------ ---------- -------------------- -------- -----------
Book Book Book {} 5 0
Book Book.IssueDate IssueDate 02-15-2019 1 1
Book Book.Detail Detail {} 5 0
Book Book.Chapter Chapter [] 4 0
Book Book.Sponsor Sponsor [] 4 0
Book Book.Hooey Hooey NULL 0 1
Book.Sponsor Book.Sponsor.0 0 A 1 1
Book.Sponsor Book.Sponsor.1 1 B 1 1
Book.Sponsor Book.Sponsor.2 2 C 1 1
Book.Sponsor Book.Sponsor.3 3 NULL 0 1
Book.Chapter Book.Chapter.0 0 {} 5 0
Book.Chapter Book.Chapter.1 1 {} 5 0
Book.Chapter.1 Book.Chapter.1.Section Section 1.2 1 1
Book.Chapter.1 Book.Chapter.1.Title Title Be happy. 1 1
Book.Chapter.0 Book.Chapter.0.Section Section 1.1 1 1
Book.Chapter.0 Book.Chapter.0.Title Title Hello world. 1 1
Book.Detail Book.Detail.Type Type Any Type 1 1
Book.Detail Book.Detail.Author Author {} 5 0
Book.Detail.Author Book.Detail.Author.Name Name Annie 1 1
Book.Detail.Author Book.Detail.Author.Sex Sex Female 1 1
(20 row(s) affected)
这显示了ParentId
,Id
和Node
,但实际上,Id
列是多余的。您不需要它来重建json
。但是,可能方便的是包括一个序列。
最后一点...我认为在cte内部进行递归比在外部进行更容易,因为您不必交叉应用函数...如参考答案所示。如果愿意,您仍然可以将其封装在函数中。
编辑(可能还有TL; NR)
我建议对节点进行排序对于以后的重组是个好主意...并且将选择放入函数中可能是可取的...然后我对此感到内:-)所以,这里'蒂斯。
结果输出不是按整体文档顺序,而是按 assembly 顺序。也就是说,对于任何给定节点,保证所有上级节点在输出中都更早...并且父节点的所有子节点都按文档顺序排列。我在函数中添加了一个相对的序号和一个深度指示符,认为这些值在某些情况下可能有用。
在递归位于cte内部的情况下,使其成为一个函数的一件好事是,结果函数可以是内联表值函数,通常比累积表变量的表值函数效率更高。
create function dbo.JsonNodes( @j nvarchar( max ) ) returns table as return
(
with nodes as
(
select
[key] ParentId,
[key] Id,
[key] Node,
[value] Val,
[type] Type,
isnull( abs( isjson( [value] ) -1 ), 1 ) IsLeaf,
1 Depth,
convert( bigint, 1 ) Seq
from
openjson( @j ) j
union all
select
nodes.Id,
nodes.Id + '.' + j.[key],
j.[key],
j.[value],
j.[type],
isnull( abs( isjson( j.[value] ) -1 ), 1 ),
nodes.Depth + 1,
row_number() over( partition by nodes.Id order by nodes.Id )
from
nodes
outer apply
openjson( nodes.Val ) j
where
isjson( nodes.Val ) = 1
)
select
ParentId,
Id,
Node,
case when Type=5 then '{}' when Type=4 then '[]' else Val end Val,
Type,
IsLeaf,
Depth,
Seq
from
nodes
)