Saya menulis parser JSON khusus di T-SQL † .
Untuk keperluan parser saya, saya menggunakan PATINDEX
fungsi yang menghitung posisi token dari daftar token. Token dalam kasus saya semuanya adalah karakter tunggal dan termasuk:
{} []:,
Biasanya, ketika saya perlu menemukan posisi (pertama) dari beberapa karakter yang diberikan, saya menggunakan PATINDEX
fungsi seperti ini:
PATINDEX('%[abc]%', SourceString)
Fungsi kemudian akan memberikan posisi pertama a
atau b
atau c
- mana kebetulan ditemukan pertama - dalam SourceString
.
Sekarang masalah dalam kasus saya tampaknya terhubung dengan ]
karakter. Segera setelah saya tentukan dalam daftar karakter, misal seperti ini:
PATINDEX('%[[]{}:,]%', SourceString)
Pola yang saya maksudkan ternyata menjadi rusak, karena fungsi tidak pernah menemukan kecocokan. Sepertinya saya perlu cara untuk melarikan diri yang pertama ]
sehingga PATINDEX
memperlakukannya sebagai salah satu karakter pencarian daripada simbol khusus.
Saya menemukan pertanyaan ini menanyakan tentang masalah yang serupa:
Namun, dalam hal ]
itu tidak perlu ditentukan dalam tanda kurung, karena itu hanya satu karakter dan dapat ditentukan tanpa tanda kurung di sekitarnya. Solusi alternatif, yang menggunakan melarikan diri, hanya berfungsi untuk LIKE
dan bukan untuk PATINDEX
, karena menggunakan ESCAPE
sub - klausa, didukung oleh yang pertama dan bukan oleh yang terakhir.
Jadi, pertanyaan saya adalah, apakah ada cara untuk mencari ]
dengan PATINDEX
menggunakan [ ]
wildcard? Atau adakah cara untuk meniru fungsi itu menggunakan alat Transact-SQL lainnya?
informasi tambahan
Berikut adalah contoh query di mana saya harus menggunakan PATINDEX
dengan […]
pola seperti di atas. Pola di sini berfungsi (meskipun agak ) karena tidak termasuk ]
karakter. Saya membutuhkannya untuk bekerja dengan ]
:
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)
;
Output yang saya dapatkan adalah:
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" }
Anda dapat melihat bahwa ]
ini dimasukkan sebagai bagian dari S
dalam salah satu baris. The Level
kolom menunjukkan tingkat bersarang, yang berarti bracket dan kawat gigi bersarang. Seperti yang Anda lihat, begitu levelnya menjadi 2, ia tidak pernah kembali ke 1. Ini akan terjadi jika saya bisa PATINDEX
mengenali ]
sebagai token.
Output yang diharapkan untuk contoh di atas adalah:
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" }
Anda dapat bermain dengan kueri ini di db <> fiddle .
† Kami menggunakan SQL Server 2014 dan tidak mungkin untuk segera memutakhirkan ke versi yang mendukung penguraian JSON secara asli. Saya bisa menulis aplikasi untuk melakukan pekerjaan itu tetapi hasil parsing perlu diproses lebih lanjut, yang menyiratkan lebih banyak pekerjaan dalam aplikasi daripada hanya parsing - jenis pekerjaan yang akan jauh lebih mudah, dan mungkin lebih efisien, dilakukan dengan skrip T-SQL, kalau saja saya bisa menerapkannya langsung ke hasil.
Sangat tidak mungkin saya bisa menggunakan SQLCLR sebagai solusi untuk masalah ini. Namun, saya tidak keberatan jika seseorang memutuskan untuk mengirim solusi SQLCLR, karena itu bisa berguna bagi orang lain.
["foo]bar”]
?