Estoy escribiendo un analizador JSON personalizado en T-SQL † .
Para el propósito de mi analizador, estoy usando la PATINDEX
función que calcula la posición de un token de una lista de tokens. Los tokens en mi caso son caracteres únicos e incluyen estos:
{} []:,
Por lo general, cuando necesito encontrar la (primera) posición de cualquiera de varios caracteres, uso la PATINDEX
función de esta manera:
PATINDEX('%[abc]%', SourceString)
La función me dará la primera posición de a
or b
o c
, lo que ocurra primero, en SourceString
.
Ahora el problema en mi caso parece estar relacionado con el ]
personaje. Tan pronto como lo especifique en la lista de caracteres, por ejemplo, así:
PATINDEX('%[[]{}:,]%', SourceString)
mi patrón deseado aparentemente se rompe, porque la función nunca encuentra una coincidencia. Parece que necesito una forma de escapar del primero ]
para que lo PATINDEX
trate como uno de los caracteres de búsqueda en lugar de un símbolo especial.
He encontrado esta pregunta preguntando sobre un problema similar:
Sin embargo, en ese caso, ]
simplemente no es necesario especificarlo entre paréntesis, ya que es solo un carácter y se puede especificar sin paréntesis. La solución alternativa, que usa escape, funciona solo para LIKE
y no para PATINDEX
, porque usa una ESCAPE
subcláusula, respaldada por la primera y no por la segunda.
Por lo tanto, mi pregunta es, ¿hay alguna manera de buscar una ]
con PATINDEX
el uso del [ ]
comodín? ¿O hay una manera de emular esa funcionalidad usando otras herramientas de Transact-SQL?
Información Adicional
Aquí hay un ejemplo de una consulta en la que necesito usar PATINDEX
con el […]
patrón como el anterior. El patrón aquí funciona (aunque de alguna manera ) porque no incluye el ]
carácter. También lo necesito para trabajar ]
:
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)
;
El resultado que obtengo es:
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" }
Puede ver que ]
se incluye como parte de S
una de las filas. La Level
columna indica el nivel de anidamiento, lo que significa anidamiento entre paréntesis y llaves. Como puede ver, una vez que el nivel se convierte en 2, nunca vuelve a 1. Lo habría hecho si pudiera hacer que PATINDEX
reconozca ]
como una ficha.
El resultado esperado para el ejemplo anterior es:
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" }
Se puede jugar con esta consulta en db <> violín .
† Estamos utilizando SQL Server 2014 y es poco probable que pronto se actualice a una versión que admita el análisis JSON de forma nativa. Podría escribir una solicitud para hacer el trabajo, pero los resultados del análisis deben procesarse aún más, lo que implica más trabajo en la aplicación que solo el análisis, el tipo de trabajo que sería mucho más fácil y probablemente más eficiente. un script T-SQL, si solo pudiera aplicarlo directamente a los resultados.
Es muy poco probable que pueda usar SQLCLR como solución para este problema. Sin embargo, no me importa si alguien decide publicar una solución SQLCLR, ya que eso podría ser útil para otros.
["foo]bar”]
?