Écrire un validateur JSON


12

Écrivez un programme qui détermine si son entrée est JSON valide .

  • Entrée: texte ASCII:[\x00-\x7F]*

    Remarque: si ASCII est problématique, n'hésitez pas à utiliser un autre encodage, mais indiquez-le dans votre message.

  • Sortie: Valid ou Invalid. Le retour à la ligne peut être omis.

  • Exemple:

    $ echo '{"key": "value"}' | ./json-validate
    Valid
    $ echo '{key: "value"}' | ./json-validate
    Invalid
    
  • Règles:

    • N'utilisez pas de bibliothèque d'analyse JSON.
    • Des solutions partiellement correctes sont autorisées, mais désapprouvées.
    • Publiez votre score de suite de tests (voir ci-dessous).

La solution correcte la plus courte l' emporte.

Veuillez exécuter json-validate-test-suite.sh sur votre programme et publier votre score. Exemple:

$ ./json-validate-test-suite.sh ./buggy-prog
fail: should be invalid:  [ 0.1e ] 
fail: should be invalid:  [ 0.1e+-1 ] 
fail: should be invalid:  [ 0.1e-+1 ] 
score: 297/300

Ressources:

  • json.org - Définition concise de la grammaire JSON avec des images faciles à suivre.
  • RFC 4627 - Spécification JSON
  • json-validate.c - Une implémentation de 200 lignes qui réussit la suite de tests.

La grammaire JSON est la suivante:

json: object | array

object: '{' members? '}'
    members: pair (',' pair)*
    pair:    string ':' value

array: '[' elements? ']'
    elements: value (',' value)*

value: string
     | number
     | object
     | array
     | 'true'
     | 'false'
     | 'null'

string: '"' char* '"'
    char: [^"\\\x00-\x1F]
        | '\' escape
    escape: ["\\/bfnrt]
          | u [0-9A-Fa-f]{4}

number: '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)?

En outre, un espace peut apparaître avant ou après l'un des six caractères structurels {}[]:,

ws = [\t\n\r ]*

Gardez à l'esprit les points suivants:

  • Soyez prudent avec des fonctions comme isspace(). L'espace dans JSON est [\t\n\r ], mais isspace()traite également \v(onglet vertical) et \f(flux de formulaire) comme un espace. Bien que le mot isdigit()puisse accepter plus que juste[0-9] , il devrait être acceptable de l'utiliser ici, car nous supposons que l'entrée est en ASCII.
  • \x7Fest techniquement un caractère de contrôle, mais le RFC JSON ne le mentionne pas (il mentionne seulement [\x00-\x1F]), et la plupart des analyseurs JSON ont tendance à accepter les \x7Fcaractères dans les chaînes. En raison de cette ambiguïté, les solutions peuvent choisir de les accepter ou non.

7
Votre note "solutions partiellement correctes permises" me fait rêver d'extraire une expression régulière d'un algorithme génétique. Je dois être fou.
JB

@JB: Ce serait génial.
Joey Adams

Juste curieux, pourquoi {key: "value"}considérer JSON invalide? C'est du javascript valide.
HoLyVieR

@HoLyVieR: J'imagine que c'est pour que JSON soit plus facile à analyser et moins ambigu pour les implémenteurs. Je ne suis pas sûr non plus que j'aime cette restriction.
Joey Adams

Quelqu'un a obtenu une copie du script de validation?
Armand

Réponses:


7

PHP: 297 285 264 253 caractères

<?=preg_match(<<<'R'
~([\h
]*)({(?1)((("([^"\\\0- ]| |\\(["\\/bfnrt]|u[\dA-Fa-f]{4}))*")(?1):(?1)((?5)|-?(0|[1-9]\d*)(\.\d+)?([Ee][+-]?\d+)?|(?2)|true|false|null))(((?1),(?1))(?4))*)?}|\[(?1)((?8)((?13)(?8))*)?(?1)])(?1)\z~A
R
,`cat`)?'Valid':'Invalid';

score: 300/300

Il s'agit d'une implémentation complète et récursive de la grammaire JSON.

Il ne fonctionne que sur PHP ≥ 5.3 en raison de Nowdoc syntaxe (heredoc aurait fallu doubler tous \).

Version lisible:

(c'est le même regex, avec des groupes de capture nommés et une syntaxe étendue):

#!/usr/bin/env php
<?php

$re = <<< 'RE'
~\A (?P<ws>[\t\n\r ])* (
    (?P<object>\{ (?P>ws)*
        (?P<members>
            (?P<pair>
                (?P<string>
                    "(?P<char>
                        [^"\\\x00-\x1F]
                        |\\(?P<escape>
                            ["\\/bfnrt]
                            |u [0-9A-Fa-f]{4}
                        )
                    )*"
                ) (?P>ws)* : (?P>ws)* (?P<value>
                    (?P>string)
                    | (?P<number>-? (0 | [1-9][0-9]*) (\. [0-9]+)? ([Ee] [+-]? [0-9]+)? )
                    | (?P>object)
                    | (?P>array)
                    | true
                    | false
                    | null
                )
            ) ( (?P>ws)* , (?P>ws)* (?P>pair) )*
        )?
    \})
    |(?P<array>\[ (?P>ws)*
        (?P<elements>
            (?P>value) ( (?P>ws)* , (?P>ws)* (?P>value) )*
        )?
    (?P>ws)* \])
) (?P>ws)* \z~x
RE;

if (preg_match($re, stream_get_contents(STDIN))) {
    echo 'Valid';
} else {
    echo 'Invalid';
}

Sensationnel. `` `` ``
Nathan Osman

Vous devez inclure l' <?phpOMI.
lâche anonyme le

Ajoutée. 264 caractères maintenant :-)
Arnaud Le Blanc

5

Python - 340 314 299 292 caractères

import re,os
r=x=re.sub
z=r('0\.0+','0',r('e[+-]?0+|[\t\n\r]',' ',r(r'"(\\["nrtb\\/]|[^\\"\0-\37])*"','1',r(r'true|false|null|\\u\w{4}|[1-9]\d*','0',os.read(0,99)))))
while z!=x:z,x=r('\{(1:\d)?(,\\1)*\}|\[(-?\d(,-?\d)*)?\]','0',r(' *([][{}:,]) *','\\1',z)),z
print['Inv','V'][z=='0']+'alid'

But

$ ./json-validate-test-suite.sh ./codegolf-474.py
score: 300/300

3

Scala - 390 caractères

import scala.util.parsing.combinator.JavaTokenParsers
object J extends JavaTokenParsers{def j=o|a
def o:Parser[Any]="{"~repsep(p,",")~"}"
def p=s~":"~v
def a:Parser[Any]="["~repsep(v,",")~"]"
def v=s|o|a|"true"|"false"|"null"
def s=stringLiteral
def n=floatingPointNumber}
object Main{def main(a:Array[String]){print(if(J.parseAll(J.j,readLine()).successful)"Valid"else"Invalid")}}

Il s'agit d'un soluton simple, utilisant des combinateurs d'analyseurs. Écrit en 1 ou 2 minutes, littéralement. Impossible d'obtenir le script de validation, le navigateur a indiqué que le serveur était introuvable.


ressemble à une solution intéressante; le lien du validateur a été corrigé.
Armand

Existe-t-il un moyen simple de le faire sous Windows? (sans cygwin ou hérésie similaire)
Nom d'affichage
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.