Pregunta Regex para validar JSON


Estoy buscando un Regex que me permita validar json.

Soy muy nuevo en Regex y sé lo suficiente que el análisis con Regex es malo, pero ¿se puede usar para validar?


74
2018-04-06 08:17


origen


Respuestas:


Sí, es posible una validación de expresiones regulares completa.

La mayoría de las implementaciones de expresiones regulares modernas permiten expresiones regulares recursivas, que pueden verificar una estructura serializada JSON completa. los especificación json.org lo hace bastante sencillo.

$pcre_regex = '
  /
  (?(DEFINE)
     (?<number>   -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )    
     (?<boolean>   true | false | null )
     (?<string>    " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )
     (?<array>     \[  (?:  (?&json)  (?: , (?&json)  )*  )?  \s* \] )
     (?<pair>      \s* (?&string) \s* : (?&json)  )
     (?<object>    \{  (?:  (?&pair)  (?: , (?&pair)  )*  )?  \s* \} )
     (?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )
  )
  \A (?&json) \Z
  /six   
';

Funciona bastante bien en PHP con el Funciones PCRE . Debería funcionar sin modificaciones en Perl; y ciertamente puede ser adaptado para otros idiomas. También tiene éxito con el Casos de prueba JSON.

Verificación RFC4627 más simple

Un enfoque más simple es la verificación de consistencia mínima como se especifica en RFC4627, sección 6. Sin embargo, está destinado como prueba de seguridad y precaución básica de no validez:

  var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
         text.replace(/"(\\.|[^"\\])*"/g, ''))) &&
     eval('(' + text + ')');

158
2017-10-02 13:04



Sí, es un concepto erróneo común que las expresiones regulares solo pueden coincidir idiomas regulares. De hecho, las funciones de PCRE pueden coincidir mucho más que los idiomas regulares, ¡pueden emparejar incluso algunos lenguajes sin contexto! Artículo de Wikipedia sobre RegExps tiene una sección especial al respecto.

¡JSON puede ser reconocido usando PCRE de varias maneras! @mario mostró una gran solución usando subpatterns con nombre y retros referencias. Luego notó que debería haber una solución usando patrones recursivos  (?R). Aquí hay un ejemplo de dicha expresión regular escrita en PHP:

$regexString = '"([^"\\\\]*|\\\\["\\\\bfnrt\/]|\\\\u[0-9a-f]{4})*"';
$regexNumber = '-?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?';
$regexBoolean= 'true|false|null'; // these are actually copied from Mario's answer
$regex = '/\A('.$regexString.'|'.$regexNumber.'|'.$regexBoolean.'|';    //string, number, boolean
$regex.= '\[(?:(?1)(?:,(?1))*)?\s*\]|'; //arrays
$regex.= '\{(?:\s*'.$regexString.'\s*:(?1)(?:,\s*'.$regexString.'\s*:(?1))*)?\s*\}';    //objects
$regex.= ')\Z/is';

Estoy usando (?1) en lugar de (?R) porque el último hace referencia al todo patrón, pero tenemos \A y \Z secuencias que no deberían usarse dentro de subpatrones. (?1) referencias a la expresión regular marcada por el paréntesis más externo (esta es la razón por la cual el más externo ( ) no comienza con ?:) Entonces, el RegExp se convierte en 268 caracteres de largo :)

/\A("([^"\\]*|\\["\\bfnrt\/]|\\u[0-9a-f]{4})*"|-?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?|true|false|null|\[(?:(?1)(?:,(?1))*)?\s*\]|\{(?:\s*"([^"\\]*|\\["\\bfnrt\/]|\\u[0-9a-f]{4})*"\s*:(?1)(?:,\s*"([^"\\]*|\\["\\bfnrt\/]|\\u[0-9a-f]{4})*"\s*:(?1))*)?\s*\})\Z/is

De todos modos, esto debe ser tratado como una "demostración de tecnología", no como una solución práctica. En PHP voy a validar la cadena JSON con llamar al json_decode() función (al igual que @Epcylon señaló). Si voy a utilizar ese JSON (si está validado), entonces este es el mejor método.


24
2018-06-06 08:25



Debido a la naturaleza recursiva de JSON (anidado {...}-s), regex no es adecuado para validarlo. Claro, algunos sabores regex pueden coincidir recursivamente patrones* (y por lo tanto puede coincidir con JSON), pero los patrones resultantes son horribles de mirar, ¡y nunca deben usarse en el código de producción IMO!

* Sin embargo, ten cuidado, muchas implementaciones de expresiones regulares hacen no apoyar patrones recursivos. De los populares lenguajes de programación, estos soportan patrones recursivos: Perl, .NET, PHP y Ruby 1.9.2


13
2018-04-06 08:21



Intenté la respuesta de @mario, pero no funcionó, porque descargué el conjunto de pruebas de JSON.org (archivo) y hubo 4 pruebas fallidas (fail1.json, fail18.json, fail25.json, fail27.json).

Investigué los errores y descubrí que fail1.json es realmente correcto (de acuerdo con el manual Nota y RFC-7159 string válido también es un JSON válido). Archivo fail18.json tampoco fue el caso, porque contiene realmente JSON profundamente anidado corregido:

[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]

Entonces quedan dos archivos: fail25.json y fail27.json:

["  tab character   in  string  "]

y

["line
break"]

Ambos contienen caracteres inválidos. Así que actualicé el patrón de esta manera (subpattern de cadena actualizado):

$pcreRegex = '/
          (?(DEFINE)
             (?<number>   -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? )
             (?<boolean>   true | false | null )
             (?<string>    " ([^"\n\r\t\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " )
             (?<array>     \[  (?:  (?&json)  (?: , (?&json)  )*  )?  \s* \] )
             (?<pair>      \s* (?&string) \s* : (?&json)  )
             (?<object>    \{  (?:  (?&pair)  (?: , (?&pair)  )*  )?  \s* \} )
             (?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) \s* )
          )
          \A (?&json) \Z
          /six';

Entonces ahora todas las pruebas legales de json.org puede ser pasado


5
2017-07-25 11:34



Creé una implementación de Ruby de la solución de Mario, que funciona:

# encoding: utf-8

module Constants
  JSON_VALIDATOR_RE = /(
         # define subtypes and build up the json syntax, BNF-grammar-style
         # The {0} is a hack to simply define them as named groups here but not match on them yet
         # I added some atomic grouping to prevent catastrophic backtracking on invalid inputs
         (?<number>  -?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?){0}
         (?<boolean> true | false | null ){0}
         (?<string>  " (?>[^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " ){0}
         (?<array>   \[ (?> \g<json> (?: , \g<json> )* )? \s* \] ){0}
         (?<pair>    \s* \g<string> \s* : \g<json> ){0}
         (?<object>  \{ (?> \g<pair> (?: , \g<pair> )* )? \s* \} ){0}
         (?<json>    \s* (?> \g<number> | \g<boolean> | \g<string> | \g<array> | \g<object> ) \s* ){0}
       )
    \A \g<json> \Z
    /uix
end

########## inline test running
if __FILE__==$PROGRAM_NAME

  # support
  class String
    def unindent
      gsub(/^#{scan(/^(?!\n)\s*/).min_by{|l|l.length}}/u, "")
    end
  end

  require 'test/unit' unless defined? Test::Unit
  class JsonValidationTest < Test::Unit::TestCase
    include Constants

    def setup

    end

    def test_json_validator_simple_string
      assert_not_nil %s[ {"somedata": 5 }].match(JSON_VALIDATOR_RE)
    end

    def test_json_validator_deep_string
      long_json = <<-JSON.unindent
      {
          "glossary": {
              "title": "example glossary",
          "GlossDiv": {
                  "id": 1918723,
                  "boolean": true,
                  "title": "S",
            "GlossList": {
                      "GlossEntry": {
                          "ID": "SGML",
                "SortAs": "SGML",
                "GlossTerm": "Standard Generalized Markup Language",
                "Acronym": "SGML",
                "Abbrev": "ISO 8879:1986",
                "GlossDef": {
                              "para": "A meta-markup language, used to create markup languages such as DocBook.",
                  "GlossSeeAlso": ["GML", "XML"]
                          },
                "GlossSee": "markup"
                      }
                  }
              }
          }
      }
      JSON

      assert_not_nil long_json.match(JSON_VALIDATOR_RE)
    end

  end
end

2
2018-05-23 20:20



Para "cadenas y números", creo que la expresión regular parcial para los números:

-?(?:0|[1-9]\d*)(?:\.\d+)(?:[eE][+-]\d+)?

debería ser en su lugar:

-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?

ya que la parte decimal del número es opcional, y también es probablemente más seguro escapar de la - símbolo en [+-] ya que tiene un significado especial entre paréntesis


1
2017-11-03 14:46



Una coma que se arrastra en una matriz JSON provocó que mi Perl 5.16 se bloqueara, posiblemente porque se mantenía retrocediendo. Tuve que agregar una directiva de terminación de reversa:

(?<json>   \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) )(*PRUNE) \s* )
                                                                                   ^^^^^^^^

De esta forma, una vez que identifica una construcción que no es 'opcional' (* o ?), no debería intentar retroceder sobre él para intentar identificarlo como algo más.


1
2017-09-14 02:17



Como se describió anteriormente, si el lenguaje que utiliza tiene una biblioteca JSON que viene con él, úselo para intentar decodificar la cadena y atrapar la excepción / error si falla. Si el idioma no funciona (solo tenía un caso como este con FreeMarker) la siguiente expresión regular podría al menos proporcionar una validación muy básica (está escrita para que PHP / PCRE sea comprobable / utilizable para más usuarios). No es tan infalible como la solución aceptada, pero tampoco es tan aterradora =):

~^\{\s*\".*\}$|^\[\n?\{\s*\".*\}\n?\]$~s

breve explicación:

// we have two possibilities in case the string is JSON
// 1. the string passed is "just" a JSON object, e.g. {"item": [], "anotheritem": "content"}
// this can be matched by the following regex which makes sure there is at least a {" at the
// beginning of the string and a } at the end of the string, whatever is inbetween is not checked!

^\{\s*\".*\}$

// OR (character "|" in the regex pattern)
// 2. the string passed is a JSON array, e.g. [{"item": "value"}, {"item": "value"}]
// which would be matched by the second part of the pattern above

^\[\n?\{\s*\".*\}\n?\]$

// the s modifier is used to make "." also match newline characters (can happen in prettyfied JSON)

si me perdí algo que rompería esto involuntariamente, ¡estoy agradecido por los comentarios!


0
2018-01-07 10:12



Mirando la documentación para JSON, parece que la expresión regular puede ser simplemente tres partes si el objetivo es solo verificar la aptitud:

  1. La cadena comienza y termina con cualquiera [] o {}
    • [{\[]{1}...[}\]]{1}
  2. y
    1. El personaje es un personaje de control JSON permitido (solo uno)
      • ...[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]...
    2. o El conjunto de caracteres contenidos en un ""
      • ...".*?"...

Todos juntos: [{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}

Si la cadena JSON contiene newline personajes, entonces deberías usar singleline encienda su sabor regex para que . partidos newline. Tenga en cuenta que esto no fallará en todos los JSON incorrectos, pero fallará si la estructura básica de JSON no es válida, que es una forma directa de realizar una validación de cordura básica antes de pasarla a un analizador.


0
2017-07-18 13:02