Regex pour valider JSON

Je recherche un Regex qui me permet de valider json.

Je suis très nouveau sur Regex et je sais assez que l’parsing avec Regex est mauvaise mais peut-elle être utilisée pour valider?

Oui, une validation complète des regex est possible.

La plupart des implémentations regex modernes permettent des expressions rationnelles récursives, qui peuvent vérifier une structure sérialisée JSON complète. La spécification json.org le rend assez simple.

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

Il fonctionne assez bien en PHP avec les fonctions PCRE . Devrait fonctionner sans modification en Perl; et peut certainement être adapté pour d’autres langues. En outre, il réussit avec les cas de test JSON .

Vérification plus simple de la RFC4627

Une approche plus simple est la vérification de la cohérence minimale spécifiée dans la RFC4627, section 6 . Il s’agit toutefois uniquement d’un test de sécurité et d’une précaution de base de non-validité:

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

Oui, c’est une idée fausse commune que les expressions régulières ne peuvent correspondre qu’à des langues régulières . En fait, les fonctions PCRE peuvent correspondre beaucoup plus que les langages normaux , elles peuvent même correspondre à certains langages non contextuels! L’article de Wikipedia sur RegExps contient une section spéciale à ce sujet.

JSON peut être reconnu en utilisant PCRE de plusieurs manières! @mario a montré une excellente solution en utilisant des sous-modèles et des références arrière nommés. Puis il a noté qu’il devrait y avoir une solution utilisant des modèles récursifs (?R) . Voici un exemple d’une telle expression rationnelle écrite en PHP:

 $regexSsortingng = '"([^"\\\\]*|\\\\["\\\\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('.$regexSsortingng.'|'.$regexNumber.'|'.$regexBoolean.'|'; //ssortingng, number, boolean $regex.= '\[(?:(?1)(?:,(?1))*)?\s*\]|'; //arrays $regex.= '\{(?:\s*'.$regexSsortingng.'\s*:(?1)(?:,\s*'.$regexSsortingng.'\s*:(?1))*)?\s*\}'; //objects $regex.= ')\Z/is'; 

J’utilise (?1) au lieu de (?R) car ce dernier fait référence à l’ ensemble du motif, mais nous avons des séquences \A et \Z qui ne doivent pas être utilisées dans les sous-motifs. (?1) références à l’expression rationnelle marquée par les parenthèses les plus à l’extérieur (c’est pourquoi le plus à l’extérieur ( ) ne commence pas par ?: . Donc, le RegExp devient long de 268 caractères 🙂

 /\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 

Quoi qu’il en soit, cela devrait être considéré comme une “démonstration technologique” et non comme une solution pratique. En PHP, je validerai la chaîne JSON en appelant la fonction json_decode() (tout comme @Epcylon). Si je vais utiliser ce JSON (s’il est validé), alors c’est la meilleure méthode.

En raison de la nature récursive de JSON (nesteds {...} -s), regex ne convient pas pour le valider. Bien sûr, certaines versions de regex peuvent correspondre de manière récursive à des patterns * (et peuvent donc correspondre à JSON), mais les patterns résultants sont horribles à regarder et ne devraient jamais être utilisés dans le code de production IMO!

* Attention cependant, de nombreuses implémentations de regex ne supportent pas les patterns récursifs. Parmi les langages de programmation populaires, ceux-ci prennent en charge les modèles récursifs: Perl, .NET, PHP et Ruby 1.9.2

J’ai essayé la réponse de @mario, mais cela n’a pas fonctionné car j’ai téléchargé une suite de tests depuis JSON.org ( archive ) et il y a eu 4 tests échoués (fail1.json, fail18.json, fail25.json, fail27. json).

J’ai étudié les erreurs et découvert que fail1.json est réellement correct (selon la note du manuel et RFC-7159, une chaîne valide est également un JSON valide). Le fichier fail18.json n’était pas le cas non plus, car il contenait en fait un JSON profondément nested:

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

Il rest donc deux fichiers: fail25.json et fail27.json :

 [" tab character in ssortingng "] 

et

 ["line break"] 

Les deux contiennent des caractères non valides. J’ai donc mis à jour le modèle comme ceci (chaîne de caractères mise à jour):

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

Donc, maintenant, tous les tests légaux de json.org peuvent être passés.

J’ai créé une implémentation Ruby de la solution de Mario, qui fonctionne:

 # 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 (? -?(?=[1-9]|0(?!\d))\d+(\.\d+)?([eE][+-]?\d+)?){0} (? true | false | null ){0} (? " (?>[^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9a-f]{4} )* " ){0} (? \[ (?> \g (?: , \g )* )? \s* \] ){0} (? \s* \g \s* : \g ){0} (? \{ (?> \g (?: , \g )* )? \s* \} ){0} (? \s* (?> \g | \g | \g | \g | \g ) \s* ){0} ) \A \g \Z /uix end ########## inline test running if __FILE__==$PROGRAM_NAME # support class Ssortingng 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 

Pour “ssortingngs and numbers”, je pense que l’expression régulière partielle pour les nombres:

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

devrait être à la place:

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

puisque la partie décimale du nombre est facultative, et qu’il est probablement plus sûr d’échapper au symbole - dans [+-] car il a une signification particulière entre parenthèses

Une virgule à la fin dans un tableau JSON a provoqué le blocage de mon Perl 5.16, peut-être parce qu’il faisait l’object d’un retour en arrière. J’ai dû append une directive de fin de backtrack:

 (? \s* (?: (?&number) | (?&boolean) | (?&ssortingng) | (?&array) | (?&object) )(*PRUNE) \s* ) ^^^^^^^^ 

De cette façon, une fois qu’il identifie une construction qui n’est pas «facultative» ( * ou ? ), Elle ne devrait pas essayer de la retracer pour essayer de l’identifier comme autre chose.

En regardant la documentation de JSON , il semble que le regex puisse simplement être composé de trois parties si l’objective est simplement de vérifier l’aptitude physique:

  1. La chaîne commence et se termine par [] ou {}
    • [{\[]{1}[}\]]{1}
  2. et
    1. Le caractère est un caractère de contrôle JSON autorisé (un seul)
      • [,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]
    2. ou L’ensemble des caractères contenus dans un ""
      • ".*?"

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

Si la chaîne JSON contient des caractères de newline , vous devez utiliser le commutateur singleline sur votre saveur regex pour que . correspond à la newline . Notez que cela n’échouera pas sur tous les JSON défectueux, mais échouera si la structure JSON de base n’est pas valide, ce qui est un moyen simple d’effectuer une validation de base avant de la transmettre à un parsingur.

Comme cela a été écrit ci-dessus, si la langue que vous utilisez contient une bibliothèque JSON, utilisez-la pour essayer de décoder la chaîne et intercepter l’exception / erreur si elle échoue! Si la langue ne le permet pas (si FreeMarker en avait un exemple), la regex suivante pourrait au moins fournir une validation très basique (écrite pour que PHP / PCRE soit testable / utilisable par un plus grand nombre d’utilisateurs). Ce n’est pas aussi infaillible que la solution acceptée, mais aussi pas si effrayant =):

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

brève explication:

 // we have two possibilities in case the ssortingng is JSON // 1. the ssortingng passed is "just" a JSON object, eg {"item": [], "anotheritem": "content"} // this can be matched by the following regex which makes sure there is at least a {" at the // beginning of the ssortingng and a } at the end of the ssortingng, whatever is inbetween is not checked! ^\{\s*\".*\}$ // OR (character "|" in the regex pattern) // 2. the ssortingng passed is a JSON array, eg [{"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 j’ai raté quelque chose qui briserait cela involontairement, je suis reconnaissant pour les commentaires!

Voici mon regexp pour validate ssortingng:

 ^\"([^\"\\]*|\\(["\\\/bfnrt]{1}|u[a-f0-9]{4}))*\"$ 

A été écrit en utilisant le diagramme de syntaxe original .

Je me rends compte que cela date de plus de 6 ans. Cependant, je pense qu’il existe une solution que personne ici n’a mentionnée, qui est beaucoup plus facile que le regexing.

 function isAJSON(ssortingng) { try { JSON.parse(ssortingng) } catch(e) { if(e instanceof SyntaxError) return false; }; return true; }