Escreva um validador JSON


12

Escreva um programa que determine se sua entrada é JSON válida .

  • Entrada: texto ASCII:[\x00-\x7F]*

    Nota: se o ASCII for problemático, fique à vontade para usar outra codificação, mas indique-a na sua postagem.

  • Saída: Valid ou Invalid. A nova linha à direita pode ser omitida.

  • Exemplo:

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

    • Não use uma biblioteca de análise JSON.
    • Soluções parcialmente corretas são permitidas, mas desaprovadas.
    • Poste sua pontuação na suíte de testes (veja abaixo).

A menor solução correta vence.

Por favor, execute json-validate-test-suite.sh no seu programa e publique sua pontuação. Exemplo:

$ ./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

Recursos:

  • json.org - Definição concisa da gramática JSON com imagens fáceis de seguir.
  • RFC 4627 - especificação JSON
  • json-validate.c - Uma implementação de 200 linhas que passa no testinguite.

A gramática JSON é a seguinte:

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]+)?

Além disso, o espaço em branco pode aparecer antes ou depois de qualquer um dos seis caracteres estruturais {}[]:,

ws = [\t\n\r ]*

Tenha em mente o seguinte:

  • Tenha cuidado com funções como isspace(). O espaço em branco no JSON é [\t\n\r ], mas isspace()também trata \v(guia vertical) e \f(alimentação do formulário) como espaço. Embora exista uma palavra que isdigit()possa aceitar mais do que apenas[0-9] , convém usar aqui, pois assumimos que a entrada está em ASCII.
  • \x7Fé tecnicamente um caractere de controle, mas o JSON RFC não o menciona (apenas menciona [\x00-\x1F]), e a maioria dos analisadores JSON costuma aceitar \x7Fcaracteres em strings. Devido a essa ambiguidade, as soluções podem optar por aceitá-las ou não.

7
Sua nota "soluções parcialmente corretas permitidas" está me fazendo sonhar em extrair uma regex de um algoritmo genético. Eu devo ser louco.
JB

@JB: Isso seria incrível.
Joey Adams

Apenas curioso, por que {key: "value"}considerar JSON inválido? É javascript válido.
51111 HoLyVieR

@HoLyVieR: Eu imagino que o JSON seja mais fácil de analisar e menos ambíguo para os implementadores. Também não tenho certeza de gostar dessa restrição.
Joey Adams

Alguém conseguiu uma cópia do script validador?
Armand

Respostas:


7

PHP: 297 285 264 253 caracteres

<?=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';

pontuação: 300/300

Esta é uma implementação completa e recursiva da gramática JSON.

Ele só funciona em PHP ≥ 5,3 devido à nowdoc sintaxe (heredoc teria exigido para dobrar tudo \).

Versão legível:

(este é o mesmo regex, com grupos de captura nomeados e sintaxe estendida):

#!/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';
}

Uau. `` `` ``
Nathan Osman

Você precisa incluir o <?phpIMO.
covarde anônimo

Adicionado. 264 caracteres agora :-)
Arnaud Le Blanc

5

Python - 340 314 299 292 caracteres

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'

Ponto

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

3

Scala - 390 caracteres

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")}}

Este é um solução simples, usando combinadores de analisador. Escrito em 1 ou 2 minutos, literalmente. Não é possível obter o script validador, o navegador disse que o servidor não foi encontrado.


parece uma solução interessante; o link do validador foi corrigido.
Armand

Existe uma maneira simples de fazer isso no Windows? (sem cygwin ou heresia semelhante)
Nome de exibição
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.