Ich schreibe einen benutzerdefinierten JSON-Parser in T-SQL † .
Für den Zweck meines Parsers verwende ich die PATINDEX
Funktion, die die Position eines Tokens aus einer Liste von Token berechnet. Die Token in meinem Fall sind alle Einzelzeichen und enthalten Folgendes:
{} []:,
Wenn ich die (erste) Position eines von mehreren gegebenen Zeichen finden muss, verwende ich normalerweise die folgende PATINDEX
Funktion:
PATINDEX('%[abc]%', SourceString)
Die Funktion gibt mir dann die erste Position von a
oder b
oder c
- je nachdem, was zuerst gefunden wird - in SourceString
.
Jetzt scheint das Problem in meinem Fall mit dem ]
Charakter verbunden zu sein. Sobald ich es in der Zeichenliste spezifiziere, zB so:
PATINDEX('%[[]{}:,]%', SourceString)
Mein beabsichtigtes Muster wird anscheinend gebrochen, weil die Funktion nie eine Übereinstimmung findet. Es sieht ]
so aus, PATINDEX
als ob ich einen Weg brauche, um dem ersten zu entkommen, damit er als eines der Suchzeichen und nicht als spezielles Symbol behandelt wird.
Ich habe diese Frage zu einem ähnlichen Problem gefunden:
In diesem Fall muss das ]
einfach nicht in Klammern angegeben werden, da es nur ein Zeichen ist und ohne Klammern angegeben werden kann. Die alternative Lösung, die Escape verwendet, funktioniert nur für LIKE
und nicht für PATINDEX
, da sie einen ESCAPE
Unterabschnitt verwendet, der von der ersteren und nicht von der letzteren unterstützt wird.
Meine Frage ist also, gibt es eine Möglichkeit, ]
mit PATINDEX
dem [ ]
Platzhalter nach einem zu suchen ? Oder gibt es eine Möglichkeit, diese Funktionalität mit anderen Transact-SQL-Tools zu emulieren?
zusätzliche Information
Hier ist ein Beispiel für eine Abfrage, bei der ich PATINDEX
das […]
Muster wie oben verwenden muss. Das Muster hier funktioniert (wenn auch etwas ), weil es das ]
Zeichen nicht enthält . Ich brauche es auch, um damit zu arbeiten ]
:
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)
;
Die Ausgabe, die ich bekomme, ist:
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" }
Sie können sehen, dass das ]
als Teil von S
in einer der Zeilen enthalten ist. Die Level
Spalte gibt die Verschachtelungsebene an, dh die Verschachtelung von Klammern und Klammern. Wie Sie sehen können, sobald die Stufe 2 wird, gibt es nie zu 1. Es wäre, wenn ich machen könnte PATINDEX
erkennen , ]
als Zeichen.
Die erwartete Ausgabe für das obige Beispiel lautet:
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" }
Sie können mit dieser Abfrage bei db <> fiddle spielen .
† Wir verwenden SQL Server 2014 und werden wahrscheinlich nicht bald auf eine Version aktualisieren, die JSON-Parsing nativ unterstützt. Ich könnte eine Anwendung schreiben, um die Arbeit zu erledigen, aber die Ergebnisse der Analyse müssen weiter verarbeitet werden, was mehr Arbeit in der Anwendung als nur die Analyse bedeutet - die Art von Arbeit, die viel einfacher und wahrscheinlich effizienter erledigt werden würde ein T-SQL-Skript, wenn ich es nur direkt auf die Ergebnisse anwenden könnte.
Es ist sehr unwahrscheinlich, dass ich SQLCLR als Lösung für dieses Problem verwenden kann. Es macht mir jedoch nichts aus, wenn sich jemand entscheidet, eine SQLCLR-Lösung zu veröffentlichen, da dies für andere nützlich sein könnte.
["foo]bar”]
?Antworten:
Meine eigene Lösung, die eher eine Problemumgehung darstellt, bestand darin, einen Zeichenbereich anzugeben, der den Bereich enthielt,
]
und diesen Bereich zusammen mit den anderen Zeichen im[ ]
Platzhalter zu verwenden. Ich habe einen Bereich verwendet, der auf der ASCII-Tabelle basiert. Gemäß dieser Tabelle befindet sich der]
Charakter in der folgenden Nachbarschaft:Mein Bereich, daher nahm die Form
[-^
, dh es vier Zeichen enthalten:[
,\
,]
,^
. Ich habe auch angegeben, dass das Muster eine binäre Kollatierung verwendet, um genau mit dem ASCII-Bereich übereinzustimmen. Der resultierendePATINDEX
Ausdruck sah folgendermaßen aus:Das offensichtliche Problem bei diesem Ansatz besteht darin, dass der Bereich am Anfang des Musters zwei unerwünschte Zeichen enthält,
\
und^
. Die Lösung funktionierte für mich einfach, weil die zusätzlichen Zeichen in den spezifischen JSON-Zeichenfolgen, die ich zum Parsen benötigte, niemals vorkommen konnten. Natürlich kann dies im Allgemeinen nicht zutreffen, daher interessiere ich mich immer noch für andere Methoden, die hoffentlich universeller sind als meine.quelle
Ich habe eine wahrscheinlich schreckliche Einstellung von hinten, als ich viel Saitensplitting machen musste.
Wenn Sie einen bekannten Zeichensatz haben, erstellen Sie eine Tabelle daraus.
Dann benutze diese Magie
CROSS APPLY
zusammen mitCHARINDEX
:Wenn mir etwas Offensichtliches fehlt, was Sie tun müssen, weiß ich Bescheid.
quelle
Ich habe in der Vergangenheit Ansätze gesehen, um den beleidigenden Charakter vor der Suche zu ersetzen und ihn anschließend wieder einzufügen.
In diesem Fall könnten wir so etwas tun:
Dieser Code gibt korrekt 5 zurück. Ich verwende das Zeichen ¬, da es unwahrscheinlich ist, dass es angezeigt wird. Wenn keine ASCII-Zeichen vorhanden sind, die Sie nicht verwenden, funktioniert diese Lösung nicht.
Seltsamerweise wäre die direkte Antwort auf Ihre Frage nein - ich kann PATINDEX auch nicht dazu bringen, nach ']' zu suchen, aber wenn Sie es ersetzen, müssen Sie es nicht.
Gleiches Beispiel, jedoch ohne variable Verwendung:
Wenn Sie die obige Lösung in Ihrem Code verwenden, erhalten Sie die gewünschten Ergebnisse:
quelle
Da
]
es nur etwas Besonderes ist[...]
, können Sie esPATINDEX
zweimal verwenden und sich]
außerhalb des bewegen[...]
. Bewerten Sie beidePATINDEX('%[[{}:,]%', SourceString)
undPATINDEX('%]%', SourceString)
. Wenn ein Ergebnis Null ist, nehmen Sie das andere. Andernfalls nehmen Sie den kleineren der beiden Werte.In Ihrem Beispiel:
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=66fba2218d8d7d310d5a682be143f6eb
quelle
Für ein linkes '[':
Für ein Recht ']':
quelle