Я пишу пользовательский анализатор 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”]
?Ответы:
Мое собственное решение, которое является скорее обходным путем, заключалось в указании диапазона символов, который включал этот диапазон,
]
и использовании этого диапазона вместе с другими символами в[ ]
шаблоне. Я использовал диапазон, основанный на таблице ASCII. Согласно этой таблице,]
персонаж находится в следующем районе:Мой диапазон, поэтому принял форму
[-^
, т.е. включены четыре символа:[
,\
,]
,^
. Я также указал, что шаблон использует двоичное сопоставление, чтобы точно соответствовать диапазону ASCII. ПолученноеPATINDEX
выражение в итоге выглядело так:Очевидная проблема с этим подходом состоит в том, что диапазон в начале шаблона включает в себя два нежелательных символа,
\
и^
. Решение сработало для меня просто потому, что дополнительные символы никогда не могли появляться в определенных строках JSON, которые мне нужно было проанализировать. Естественно, это не может быть правдой вообще, поэтому я все еще интересуюсь другими методами, надеюсь, более универсальными, чем мои.источник
Я, вероятно, ужасно воспринял это со спины, когда мне пришлось много разбивать строки.
Если у вас есть известный набор символов, составьте таблицу из них.
Тогда используйте это волшебство
CROSS APPLY
вместе сCHARINDEX
:Если я упускаю что-то очевидное в том, что тебе нужно сделать, дай мне знать.
источник
В прошлом я видел подходы, позволяющие заменить оскорбительного персонажа перед поиском, а затем вернуть его обратно.
В этом случае мы могли бы сделать что-то вроде:
Этот код правильно возвращает 5. Я использую символ ¬, поскольку он вряд ли появится - если нет символов ASCII, которые вы не будете использовать, это решение не будет работать.
Как ни странно, прямого ответа на ваш вопрос было бы нет - я не могу заставить PATINDEX искать ']', но если вы замените его, вам не нужно.
Тот же пример, но без использования переменной:
Использование вышеуказанного решения в вашем коде дает требуемые результаты:
источник
Так как
]
это только особый[...]
, вы можете использоватьPATINDEX
дважды, выходя]
за пределы[...]
. Оцените и тоPATINDEX('%[[{}:,]%', SourceString)
и другоеPATINDEX('%]%', SourceString)
. Если один результат равен нулю, возьмите другой. В противном случае возьмите меньшее из двух значений.В вашем примере:
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=66fba2218d8d7d310d5a682be143f6eb
источник
Для левого '[':
За право ']':
источник