Piszę niestandardowy parser JSON w języku T-SQL † .
Na potrzeby mojego parsera używam PATINDEX
funkcji, która oblicza pozycję tokena na podstawie listy tokenów. Wszystkie tokeny w moim przypadku są pojedynczymi postaciami i obejmują one:
{} []:,
Zwykle, gdy muszę znaleźć (pierwszą) pozycję dowolnego z kilku podanych znaków, używam PATINDEX
funkcji w następujący sposób:
PATINDEX('%[abc]%', SourceString)
Funkcja będzie następnie dać mi pierwszą pozycję a
lub b
lub c
- w zależności od tego co dzieje się znaleźć pierwszy - w SourceString
.
Teraz problem w moim przypadku wydaje się być związany z ]
postacią. Jak tylko podam to na liście postaci, np. Tak:
PATINDEX('%[[]{}:,]%', SourceString)
mój zamierzony wzorzec najwyraźniej zostaje zepsuty, ponieważ funkcja nigdy nie znajdzie dopasowania. Wygląda na to, że potrzebuję sposobu na ucieczkę od pierwszego, ]
więc PATINDEX
traktuje to jako jedną z postaci, a nie specjalny symbol.
Znalazłem to pytanie z pytaniem o podobny problem:
Jednak w takim przypadku po ]
prostu nie trzeba podawać w nawiasach, ponieważ jest to tylko jeden znak i można go podać bez nawiasów wokół nich. Alternatywne rozwiązanie, które korzysta ze znaku ucieczki, działa tylko dla, LIKE
a nie dla PATINDEX
, ponieważ wykorzystuje ESCAPE
podklucz, obsługiwany przez to pierwsze, a nie drugie.
Więc moje pytanie brzmi, czy jest jakiś sposób, aby szukać ]
z PATINDEX
użyciem [ ]
symboli wieloznacznych? Czy istnieje sposób na emulowanie tej funkcjonalności przy użyciu innych narzędzi Transact-SQL?
Dodatkowe informacje
Oto przykład z zapytaniem gdzie muszę korzystać PATINDEX
ze […]
wzoru jak powyżej. Wzór tutaj działa (choć nieco ), ponieważ nie zawiera ]
znaku. Potrzebuję go również do pracy z ]
:
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)
;
Otrzymuję wynik:
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" }
Możesz zobaczyć, że ]
jest uwzględniony jako część S
w jednym z wierszy. Level
Kolumna wskazuje poziom zagnieżdżenia, co oznacza, wspornik i szelki lęgowych. Jak widać, gdy poziom osiągnie 2, nigdy nie powróci do 1. Byłoby tak, gdybym mógł PATINDEX
rozpoznać ]
jako token.
Oczekiwany wynik dla powyższego przykładu to:
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" }
Możesz grać z tym zapytaniem w db <> skrzypce .
† Korzystamy z programu SQL Server 2014 i jest mało prawdopodobne, aby wkrótce zaktualizowano go do wersji obsługującej natywnie parsowanie JSON. Mógłbym napisać aplikację do wykonania zadania, ale wyniki analizy muszą być dalej przetwarzane, co oznacza więcej pracy w aplikacji niż tylko analizowanie - taki rodzaj pracy, który byłby o wiele łatwiejszy i prawdopodobnie bardziej wydajny, skrypt T-SQL, gdybym tylko mógł zastosować go bezpośrednio do wyników.
Jest bardzo mało prawdopodobne, że mogę użyć SQLCLR jako rozwiązania tego problemu. Jednak nie mam nic przeciwko, jeśli ktoś zdecyduje się opublikować rozwiązanie SQLCLR, ponieważ może to być przydatne dla innych.
["foo]bar”]
?