Sto scrivendo un parser JSON personalizzato in T-SQL † .
Ai fini del mio parser, sto usando la PATINDEX
funzione che calcola la posizione di un token da un elenco di token. I token nel mio caso sono tutti caratteri singoli e includono questi:
{} []:,
Di solito, quando ho bisogno di trovare la (prima) posizione di uno qualsiasi dei vari caratteri, uso la PATINDEX
funzione in questo modo:
PATINDEX('%[abc]%', SourceString)
La funzione mi darà quindi la prima posizione di a
o b
o c
- qualunque sia la prima volta che si trova - in SourceString
.
Ora il problema nel mio caso sembra essere collegato al ]
personaggio. Non appena lo specifico nell'elenco dei caratteri, ad esempio in questo modo:
PATINDEX('%[[]{}:,]%', SourceString)
il mio modello previsto apparentemente si spezza, perché la funzione non trova mai una corrispondenza. Sembra che ho bisogno di un modo per sfuggire al primo in ]
modo che lo PATINDEX
tratti come uno dei personaggi di ricerca piuttosto che un simbolo speciale.
Ho trovato questa domanda chiedendo un problema simile:
Tuttavia, in tal caso ]
non è necessario specificare semplicemente tra parentesi, poiché è solo un carattere e può essere specificato senza parentesi attorno ad essi. La soluzione alternativa, che utilizza l'escaping, funziona solo per LIKE
e non per PATINDEX
, perché utilizza un ESCAPE
sottoclauso, supportato dal primo e non dal secondo.
Quindi, la mia domanda è, c'è un modo per cercare una ]
con PATINDEX
utilizzando il [ ]
carattere jolly? O c'è un modo per emulare quella funzionalità usando altri strumenti Transact-SQL?
Informazioni aggiuntive
Ecco un esempio di una query in cui devo utilizzare PATINDEX
il […]
modello come sopra. Il modello qui funziona (anche se in qualche modo ) perché non include il ]
personaggio. Ho bisogno che funzioni anche con ]
:
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)
;
L'output che ottengo è:
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" }
Puoi vedere che ]
è incluso come parte di S
in una delle righe. La Level
colonna indica il livello di annidamento, ovvero l'annidamento di parentesi quadre e parentesi graffe. Come puoi vedere, una volta che il livello diventa 2, non torna mai a 1. Avrebbe potuto farlo PATINDEX
riconoscere ]
come token.
L'output previsto per l'esempio sopra è:
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" }
Puoi giocare con questa query su db <> fiddle .
† Stiamo utilizzando SQL Server 2014 e è improbabile che presto eseguiamo l'aggiornamento a una versione che supporta l'analisi JSON in modo nativo. Potrei scrivere un'applicazione per fare il lavoro, ma i risultati dell'analisi devono essere ulteriormente elaborati, il che implica più lavoro nell'applicazione rispetto al solo analisi - il tipo di lavoro che sarebbe molto più facile, e probabilmente più efficiente, fatto con uno script T-SQL, se solo potessi applicarlo direttamente ai risultati.
È molto improbabile che io possa usare SQLCLR come soluzione per questo problema. Tuttavia, non mi importa se qualcuno decide di pubblicare una soluzione SQLCLR, dal momento che potrebbe essere utile per gli altri.
["foo]bar”]
?