103 votes

Regex pour valider JSON

Je cherche une Regex qui me permette de valider le json.

Je suis très novice en matière de Regex et je sais assez bien que l'analyse syntaxique avec Regex est mauvaise, mais peut-on l'utiliser pour valider ?

32 votes

Pourquoi s'embêter avec une étape de validation distincte ? La plupart des langages ont des bibliothèques JSON qui peuvent analyser le JSON, et si elles peuvent le faire, c'est qu'il est valide. Si ce n'est pas le cas, la bibliothèque vous le dira.

0 votes

Vous devez analyser le texte afin de le valider...

0 votes

@mario - Quel est l'intérêt de la prime ici ? Cherchez-vous plus de réponses, ou simplement de l'attention pour votre cause ? :)

200voto

mario Points 76989

Oui, une validation regex complète est possible.

La plupart des implémentations modernes de regex permettent des regexpressions récursives, qui peuvent vérifier une structure sérialisée JSON complète. Le site Spécification de json.org rend les choses assez simples.

$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   
';

Cela fonctionne très bien en PHP avec la fonction Fonctions PCRE . devrait fonctionner sans modification en Perl ; et peut certainement être adapté à d'autres langages. Il réussit également avec le Cas de test JSON .

Vérification simplifiée de la RFC4627

Une approche plus simple est le contrôle de cohérence minimal tel que spécifié dans le document RFC4627, section 6 . Il s'agit toutefois d'un test de sécurité et d'une précaution de base contre la non-validité :

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

27 votes

+1 Il y a tellement de mal dans le monde de la part de personnes qui ne comprennent pas la syntaxe regex et en font une raison pour les détester :(

9 votes

@mario, je ne suis pas sûr que vous pensiez que je suis dans les le département des rabat-joie mais je ne le suis pas. Notez que votre déclaration "La plupart des implémentations modernes de regex permettent des regexpressions récursives" est très discutable. À ma connaissance, seuls Perl, PHP et .NET ont la capacité de définir des modèles récursifs. Je n'appellerais pas cela "la plupart".

3 votes

@Bart : Oui, c'est discutable à juste titre. Le plus ironique est que les moteurs de regex Javascript ne peuvent pas utiliser une telle regex récursive pour vérifier JSON (ou seulement avec des solutions de contournement élaborées). Donc si regex == posix regex, ce n'est pas une option. Il est néanmoins intéressant de constater que c'est faisable avec les implémentations contemporaines, même avec peu de cas pratiques. (Mais il est vrai que libpcre n'est pas le moteur prédominant partout.) -- Pour mémoire également : J'espérais un badge d'inversion synthétique, mais le fait que vous n'obteniez pas quelques upvotes de la part du bandwagon vous en empêche. :/

35voto

Hrant Khachatrian Points 1881

Oui, c'est une erreur commune que les Expressions Régulières peuvent correspondre seulement à langues régulières . En effet, les fonctions PCRE peuvent correspondre à bien plus que des langages réguliers Ils peuvent même correspondre à certains langages sans contexte ! Article de Wikipedia sur les RegExps a une section spéciale à ce sujet.

JSON peut être reconnu par PCRE de plusieurs façons ! @mario a montré une excellente solution en utilisant des sous-modèles nommés et des modèles de type rétro-références . Il a ensuite fait remarquer qu'il devrait y avoir une solution utilisant motifs récursifs (?R) . Voici un exemple d'une telle regexp écrite 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';

J'utilise (?1) au lieu de (?R) car ce dernier fait référence à la tout le site mais nous avons \A et \Z les séquences qui ne doivent pas être utilisées à l'intérieur des sous-modèles. (?1) des références à l'expression rationnelle marquée par les parenthèses les plus extérieures (c'est la raison pour laquelle l'expression rationnelle la plus extérieure ( ) ne commence pas par ?: ). Ainsi, la RegExp devient longue 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

De toute façon, ceci doit être considéré comme une "démonstration technologique", et non comme une solution pratique. En PHP, je vais valider la chaîne JSON en appelant la fonction json_decode() (comme l'a noté @Epcylon). Si je dois utiliser ce JSON (s'il est validé), alors c'est la meilleure méthode.

1 votes

Utilisation de \d est dangereux. Dans de nombreuses implémentations de regexp \d correspond à la définition Unicode d'un chiffre qui n'est pas seulement [0-9] mais inclut à la place des scripts alternatifs.

0 votes

@dolmen : vous avez peut-être raison, mais vous ne devriez pas l'éditer vous-même dans la question. L'ajouter en commentaire devrait suffire.

0 votes

Je pense \d ne correspond pas aux nombres unicode dans l'implémentation de PHP du PCRE. Par exemple (0x669 arabic-indic digit nine) sera recherché en utilisant le motif #\p{Nd}#u mais pas #\d#u

15voto

Bart Kiers Points 79069

En raison de la nature récursive de JSON (imbriqués {...} -s), regex n'est pas adapté pour le valider. Bien sûr, certaines formes de regex peuvent faire correspondre des motifs de manière récursive. * (et peut donc correspondre à JSON), mais les modèles résultants sont horribles à regarder, et ne devraient jamais être utilisés dans un code de production IMO !

* Attention toutefois, de nombreuses implémentations de regex font pas supportent les motifs récursifs. Parmi les langages de programmation les plus populaires, ceux-ci supportent les motifs récursifs : Perl, .NET, PHP et Ruby 1.9.2.

4 votes

18 votes

@ tous les votants : "regex n'est pas adapté pour le valider" ne signifie pas que certains moteurs regex ne peuvent pas le faire (du moins, c'est ce que je voulais dire). Bien sûr, certaines implémentations de regex peut mais toute personne saine d'esprit utiliserait simplement un analyseur syntaxique JSON. Tout comme si quelqu'un demandait comment construire une maison complète avec seulement un marteau, je répondrais qu'un marteau n'est pas adapté à ce travail, qu'il faut une boîte à outils complète et des machines. Bien sûr, quelqu'un avec assez d'endurance peut le faire avec un simple marteau.

3 votes

Cet avertissement est peut-être valable, mais il ne répond pas à la question . Regex n'est peut-être pas le bon outil, mais certaines personnes n'ont pas le choix. Nous sommes enfermés dans le produit d'un fournisseur qui évalue la sortie d'un service pour vérifier sa santé, et la seule option que le fournisseur fournit pour un contrôle de santé personnalisé est un formulaire Web qui accepte une expression rationnelle. Le produit du fournisseur qui évalue l'état du service n'est pas sous le contrôle de mon équipe. Pour nous, l'évaluation de JSON avec regex est maintenant une exigence, donc, une réponse de "inadéquat" n'est pas viable. (Je ne vous ai toujours pas downvoté).

2voto

Gumbo Points 279147

Vous ne pouvez pas utiliser une seule expression régulière pour décrire toutes les chaînes JSON valides. Comme JSON est un langage irrégulier en raison des objets {} et les tableaux [] qui peuvent être imbriqués arbitrairement, mais les expressions régulières classiques ne peuvent pas décrire de motifs récursifs (bien qu'il existe des implémentations modernes d'expressions régulières qui le peuvent). Il n'existe donc aucune expression régulière (classique) capable de décrire une chaîne JSON valide.

Mais vous pouvez utiliser une série d'expressions régulières pour vérifier si une chaîne est JSON. Voici un exemple utilisant Javascript :

// array or object
var re = [
    // is array or object
    /^\s*(?:\[.*]|\{.*\})\s*$/,
    // strings and numbers
    /"(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})*)"|-?(?:0|[1-9]\d*)(?:\.\d+)(?:[eE][+-]\d+)?/g,
    // "empty" arrays and objects without values like "[,,,]" and "{:,:,:,:}"
    /\s*(?:\[\s*(?:,\s*)*]|\{(?:\s*:\s*(?:,\s*:\s*)*)?})/g
];
var isArrayOrObject = re[0].test(str), tmp;
// remove strings and numbers
str = str.replace(re[1], "");
// remove "empty" arrays and objects
while ((tmp = str.replace(re[2], "")) !== str) {
    str = tmp;
}
alert(isArrayOrObject && /^\s*$/.test(str));

Cette fonction vérifie si la chaîne JSON est un tableau ou un objet. En supprimant les chaînes et les nombres, il ne reste que des tableaux "vides" et des objets sans valeur, qui peuvent ensuite être supprimés jusqu'à ce qu'il ne reste que des espaces :

1. {"foo":"bar", "baz":[0,2]}
2. {:, :[,]}
3. {:, :}
4. ε

1voto

Mikaeru Points 11

Pour "chaînes et chiffres", je pense que l'expression régulière partielle pour les chiffres :

-?(?: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 à l'élément - symbole dans [+-] puisqu'il a une signification particulière entre parenthèses

0 votes

Utilisation de \d est dangereux. Dans de nombreuses implémentations de regexp \d correspond à la définition Unicode d'un chiffre qui n'est pas seulement [0-9] mais inclut à la place des scripts alternatifs.

0 votes

Cela semble un peu étrange, que -0 soit un nombre valide, mais la RFC 4627 l'autorise et votre expression régulière s'y conforme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X