如何使用FOR JSON以这种格式构建JSON?

时间:2020-01-22 10:56:39

标签: json sql-server sql-server-2016 for-json

我想使用FOR JSON为HTTP Post调用构建数据有效负载。可以使用以下代码段重新创建“我的源代码”表:

drop table if exists #jsonData;
drop table if exists #jsonColumns;

select
    'carat' [column] 
into #jsonColumns
union
select 'cut' union
select 'color' union
select 'clarity' union
select 'depth' union
select 'table' union
select 'x' union
select 'y' union
select 'z'

select
    0.23 carat
    ,'Ideal' cut
    ,'E' color
    ,'SI2' clarity
    ,61.5 depth
    ,55.0 [table]
    ,3.95 x
    ,3.98 y
    ,2.43 z
into #jsonData
union
select 0.21,'Premium','E','SI1',59.8,61.0,3.89,3.84,2.31 union
select 0.29,'Premium','I','VS2',62.4,58.0,4.2,4.23,2.63 union
select 0.31,'Good','J','SI2',63.3,58.0,4.34,4.35,2.75
;

数据的格式需要如下:

{
    "columns":["carat","cut","color","clarity","depth","table","x","y","z"],
    "data":[
        [0.23,"Ideal","E","SI2",61.5,55.0,3.95,3.98,2.43],
        [0.21,"Premium","E","SI1",59.8,61.0,3.89,3.84,2.31],
        [0.23,"Good","E","VS1",56.9,65.0,4.05,4.07,2.31],
        [0.29,"Premium","I","VS2",62.4,58.0,4.2,4.23,2.63],
        [0.31,"Good","J","SI2",63.3,58.0,4.34,4.35,2.75]
    ]
}

到目前为止,我的尝试如下:

select
    (select * from #jsonColumns for json path) as [columns],
    (select * from #jsonData for json path) as [data]
for json path, without_array_wrapper

但是,这将返回 objects 而不是 values 的数组,如下所示:

{
    "columns":[
        {"column":"carat"},
        {"column":"clarity"},
        {"column":"color"},
        {"column":"cut"},
        {"column":"depth"},
        {"column":"table"},
        {"column":"x"},
        {"column":"y"},
        {"column":"z"}
    ]...
}

如何将数组限制为仅显示值?

1 个答案:

答案 0 :(得分:1)

老实说,这似乎比使用JSON功能更容易进行字符串聚合。

由于使用的是SQL Server 2016,因此无权访问STRING_AGGCONCAT_WS,因此代码要长得多。您必须改为使用FOR XML PATHSTUFF并手动插入所有分隔符(为什么','表达式中有这么多CONCAT)。结果如下:

DECLARE @CRLF nchar(2) = NCHAR(13) + NCHAR(10);

SELECT N'{' + @CRLF +
       N'    "columns":[' + STUFF((SELECT ',' + QUOTENAME(c.[name],'"')
                                   FROM tempdb.sys.columns c
                                        JOIN tempdb.sys.tables t ON c.object_id = t.object_id
                                   WHERE t.[name] LIKE N'#jsonData%' --Like isn't needed if not a temporary table. Use the literal name.
                                   ORDER BY c.column_id ASC
                                   FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,1,N'') + N'],' + @CRLF +
       N'    "data":[' + @CRLF +
       STUFF((SELECT N',' + @CRLF +
                     N'       ' + CONCAT('[',JD.carat,',',QUOTENAME(JD.cut,'"'),',',QUOTENAME(JD.color,'"'),',',QUOTENAME(JD.clarity,'"'),',',JD.depth,',',JD.[table],',',JD.x,',',JD.y,',',JD.z,']')
              FROM #jsonData JD
              ORDER BY JD.carat ASC
              FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + @CRLF +
      N'    ]' + @CRLF +
      N'}';

DB<>Fiddle