有没有办法使用FOR JSON返回字符串或嵌入式JSON?

时间:2019-06-10 22:54:54

标签: sql-server tsql for-json

我有一个nvarchar列,如果内容是有效的JSON,我想返回嵌入在JSON结果中,否则返回字符串。

这是我尝试过的:

select
  (
    case when IsJson(Arguments) = 1 then 
      Json_Query(Arguments) 
    else 
      Arguments 
    end
  ) Results
  from Unit
  for json path

这总是将结果放入字符串中。

以下内容有效,但前提是该属性包含有效的JSON:

select
    (
      Json_Query(
        case when IsJson(Arguments) = 1 then 
          Arguments 
        else 
          '"' + String_escape(IsNull(Arguments, ''), 'json') + '"' end
      )
    ) Results
    from Unit
    for json path

如果Arguments不包含JSON对象,则会发生运行时错误。

更新:示例数据:

Arguments
---------
{ "a": "b" }
Some text

更新:任何版本的SQL Server都可以。我什至很高兴知道它即将发布Beta版。

2 个答案:

答案 0 :(得分:2)

我找不到一个好的解决方案,并且会很高兴,如果有人带来比这种黑客更好的解决方案:

DECLARE @tbl TABLE(ID INT IDENTITY,Arguments NVARCHAR(MAX));
INSERT INTO @tbl VALUES
 (NULL)
,('plain text')
,('[{"id":"1"},{"id":"2"}]');

SELECT t1.ID
      ,(SELECT Arguments FROM @tbl t2 WHERE t2.ID=t1.ID AND ISJSON(Arguments)=0) Arguments
      ,(SELECT JSON_QUERY(Arguments) FROM @tbl t2 WHERE t2.ID=t1.ID AND ISJSON(Arguments)=1) ArgumentsJSON
FROM @tbl t1 
FOR JSON PATH;

由于省略了NULL值,所以最终结果中总是会出现ArgumentsArgumentsJSON。将此JSON视为NVARCHAR(MAX),可以使用REPLACE将所有名称重命名为相同的Arguments

问题似乎是,您不能在SELECT内包括两个具有相同名称的列,但每个列必须具有可预测的类型。这取决于您在CASE(或COALESCE)中使用的顺序。如果引擎认为“好,这是文本”,则所有内容都将被视为文本,并且您的JSON被转义。但是,如果引擎认为“好的,一些JSON”,则所有内容都将作为JSON处理,如果该JSON无效,则会中断。

对于FOR XML PATH,使用namig列有一些技巧(例如,[*][node()]甚至在一个查询中是同一查询的两倍),但是FOR JSON PATH并不那么强大...

答案 1 :(得分:1)

当您说语句“ ...始终将结果放入字符串中。” 时,您可能意味着当JSON存储在文本列中时,{{1} }对此文本进行转义。当然,如果要返回未转义的FOR JSON文本,则只需要对有效的JSON文本使用JSON_QUERY函数。

接下来是一个小变通方法(基于JSON和字符串操作),可能有助于解决您的问题。

表格:

FOR JSON

声明:

CREATE TABLE #Data (
   Arguments nvarchar(max)
)
INSERT INTO #Data 
   (Arguments)
VALUES
   ('{"a": "b"}'),
   ('Some text'),
   ('{"c": "d"}'),
   ('{"e": "f"}'),
   ('More[]text')

输出:

SELECT CONCAT(N'[', j1.JsonOutput, N',', j2.JsonOutput, N']')
FROM 
(
   SELECT JSON_QUERY(Arguments) AS Results
   FROM #Data
   WHERE ISJSON(Arguments) = 1
   FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) j1 (JsonOutput),
(
   SELECT STRING_ESCAPE(ISNULL(Arguments, ''), 'json') AS Results
   FROM #Data
   WHERE ISJSON(Arguments) = 0
   FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) j2 (JsonOutput)

注意:

这里的一个缺点是生成的输出中项目的顺序与表中的顺序不同。