Я пишу пользовательский анализатор JSON в T-SQL † .
Для моего парсера я использую PATINDEX
функцию, которая вычисляет позицию токена из списка токенов. В моем случае все токены состоят из отдельных символов и включают в себя:
{} []:,
Обычно, когда мне нужно найти (первую) позицию любого из нескольких заданных символов, я использую такую PATINDEX
функцию:
PATINDEX('%[abc]%', SourceString)
Затем функция выдаст мне первую позицию a
или b
или c
- в зависимости от того, что окажется первым - в SourceString
.
Теперь проблема в моем случае, кажется, связана с ]
персонажем. Как только я укажу это в списке символов, например, вот так:
PATINDEX('%[[]{}:,]%', SourceString)
мой предполагаемый шаблон, очевидно, становится неработающим, потому что функция никогда не находит соответствия. Похоже, мне нужен способ избежать первого, ]
чтобы PATINDEX
он воспринимался как один из символов поиска, а не как специальный символ.
Я нашел этот вопрос, спрашивая о похожей проблеме:
Однако в этом случае ]
просто не нужно указывать в скобках, потому что это всего лишь один символ, и его можно указывать без скобок вокруг них. Альтернативное решение, которое использует экранирование, работает только для, LIKE
а не для PATINDEX
, потому что оно использует ESCAPE
подпункт, поддерживаемый первым, а не последним.
Итак, мой вопрос, есть ли способ найти ]
с PATINDEX
использованием [ ]
подстановочного знака? Или есть способ эмулировать эту функциональность, используя другие инструменты Transact-SQL?
Дополнительная информация
Ниже приведен пример запроса , когда мне нужно использовать PATINDEX
с […]
рисунком , как указаны выше. Шаблон здесь работает (хотя и несколько ), потому что он не включает в себя ]
символ. Мне нужно, чтобы он ]
тоже работал:
WITH
data AS (SELECT CAST('{"f1":["v1","v2"],"f2":"v3"}' AS varchar(max)) AS ResponseJSON),
parser AS
(
SELECT
Level = 1,
OpenClose = 1,
P = p.P,
S = SUBSTRING(d.ResponseJSON, 1, NULLIF(p.P, 0) - 1),
C = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0), 1),
ResponseJSON = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0) + 1, 999999)
FROM
data AS d
CROSS APPLY (SELECT PATINDEX('%[[{]%', d.ResponseJSON)) AS p (P)
UNION ALL
SELECT
Level = ISNULL(d.OpenClose - 1, 0) + d.Level + ISNULL(oc.OpenClose, 0),
OpenClose = oc.OpenClose,
P = d.P + p.P,
S = SUBSTRING(d.ResponseJSON, 1, NULLIF(p.P, 0) - 1),
C = c.C,
ResponseJSON = SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0) + 1, 999999)
FROM
parser AS d
CROSS APPLY (SELECT PATINDEX('%[[{}:,]%' COLLATE Latin1_General_BIN2, d.ResponseJSON)) AS p (P)
CROSS APPLY (SELECT SUBSTRING(d.ResponseJSON, NULLIF(p.P, 0), 1)) AS c (C)
CROSS APPLY (SELECT CASE WHEN c.C IN ('[', '{') THEN 1 WHEN c.C IN (']', '}') THEN 0 END) AS oc (OpenClose)
WHERE 1=1
AND p.P <> 0
)
SELECT
*
FROM
parser
OPTION
(MAXRECURSION 0)
;
Я получаю вывод:
Level OpenClose P S C ResponseJSON
----- --------- -- ----- -- ---------------------------
1 1 1 { "f1":["v1","v2"],"f2":"v3"}
1 null 6 "f1" : ["v1","v2"],"f2":"v3"}
2 1 7 [ "v1","v2"],"f2":"v3"}
2 null 12 "v1" , "v2"],"f2":"v3"}
2 null 18 "v2"] , "f2":"v3"}
2 null 23 "f2" : "v3"}
2 0 28 "v3" }
Вы можете видеть, что ]
он включен как часть S
одной из строк. Level
Колонка показывает уровень вложенности, то есть кронштейн и брекеты вложенности. Как вы можете видеть, когда уровень становится 2, он никогда не возвращается к 1. Это было бы, если бы я мог сделать PATINDEX
распознавание ]
в качестве токена.
Ожидаемый результат для приведенного выше примера:
Level OpenClose P S C ResponseJSON
----- --------- -- ---- -- ---------------------------
1 1 1 { "f1":["v1","v2"],"f2":"v3"}
1 NULL 6 "f1" : ["v1","v2"],"f2":"v3"}
2 1 7 [ "v1","v2"],"f2":"v3"}
2 NULL 12 "v1" , "v2"],"f2":"v3"}
2 0 17 "v2" ] ,"f2":"v3"}
1 NULL 18 , "f2":"v3"}
1 NULL 23 "f2" : "v3"}
1 0 28 "v3" }
Вы можете поиграть с этим запросом на db <> fiddle .
† Мы используем SQL Server 2014 и вряд ли скоро обновимся до версии, которая изначально поддерживает синтаксический анализ JSON. Я мог бы написать приложение, чтобы выполнить работу, но результаты парсинга нужно обрабатывать дальше, что подразумевает больше работы в приложении, чем просто парсинг - такой вид работы, который будет гораздо проще и, вероятно, более эффективен, выполняется с помощью сценарий T-SQL, если бы я мог применить его непосредственно к результатам.
Маловероятно, что я могу использовать SQLCLR в качестве решения этой проблемы. Однако я не против, если кто-то решит опубликовать решение SQLCLR, так как это может быть полезно для других.
["foo]bar”]
?