有没有办法使用 FOR JSON return 字符串或嵌入 JSON?

Is there a way to return either a string or embedded JSON using FOR JSON?

我有一个 nvarchar 列,如果内容有效 JSON,我想将其 return 嵌入到我的 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

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

更新:示例数据:

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

更新:SQL 服务器的任何版本都可以。我什至很高兴知道它即将推出测试版或其他内容。

我没有找到好的解决方案,如果有人提出比这个 hack 更好的解决方案,我会很高兴:

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-values 被省略,您将始终在最终结果中找到 ArgumentsArgumentsJSON。将此 JSON 视为 NVARCHAR(MAX) 您可以使用 REPLACE 将所有重命名为相同的 Arguments.

问题似乎是,您不能在 SELECT 中包含两个同名的列,但每个列都必须具有可预测的类型。这取决于您在 CASE(或 COALESCE)中使用的顺序。如果引擎认为 "Okay, here's text",所有内容都将被视为文本,您的 JSON 将被转义。但是,如果引擎认为 "Okay, some JSON",所有内容都将作为 JSON 处理,如果此 JSON 无效,则会中断。

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

当您说您的语句 "... 总是将结果放入字符串中。",您的意思可能是当 JSON 存储在文本列中时, FOR JSON 转义此文本。当然,如果你想 return 一个未转义的 JSON 文本,你只需要对有效的 JSON 文本使用 JSON_QUERY 函数。

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

Table:

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)

输出:

[{"Results":{"a": "b"}},{"Results":{"c": "d"}},{"Results":{"e": "f"}},{"Results":"Some text"},{"Results":"More[]text"}]

备注:

此处的一个缺点是生成的输出中的项目顺序与 table 中的顺序不同。