Como verificar se uma string é uma string JSON válida em JavaScript sem usar Try / Catch

548

Algo como:

var jsonString = '{ "Id": 1, "Name": "Coke" }';

//should be true
IsJsonString(jsonString);

//should be false
IsJsonString("foo");
IsJsonString("<div>foo</div>")

A solução não deve conter tentativa / captura. Alguns de nós ativam a opção "interromper todos os erros" e não gostam do quebra do depurador nessas cadeias JSON inválidas.

Chi Chan
fonte
25
Existe um motivo válido para não usar try?
Nick T
7
@ NickT Porque se você ativar "interromper todos os erros" no depurador, ele ativará. O Chrome agora tem a opção de corrigir erros não detectados.
Chi Chan
6
Use apenas 2 linhas para verificar com try catch. var isValidJSON = true; tente {JSON.parse (jsonString)} catch {isValidJSON = false; }
efkan 30/08/16
18
Enquanto isso funciona, é uma prática terrivelmente desleixada e ruim. Try / catch é destinado a comportamento excepcional e manipulação de erros, não ao fluxo geral do programa.
Tasgall
7
@ Tasgall Como regra geral, sim. Mas o que você faz se a abordagem try / catch é mais eficiente do que qualquer abordagem baseada em validador? Vá com a opção (às vezes significativamente) mais lenta apenas porque a alternativa é "má prática"? Não há nada funcionalmente errado com o método try / catch, portanto não há razão para não usá-lo. É importante que novos programadores desenvolvam bons padrões de codificação, mas é igualmente importante não reforçar a adesão cega às diretrizes convencionais, especialmente nos casos em que as diretrizes tornam as coisas mais difíceis do que precisam.
precisa saber é o seguinte

Respostas:

172

Um comentário primeiro. A questão era sobre não usar try/catch.
Se você não se importa de usá-lo, leia a resposta abaixo. Aqui, apenas verificamos uma JSONstring usando um regexp, e ele funcionará na maioria dos casos, não em todos os casos.

Dê uma olhada na linha 450 em https://github.com/douglascrockford/JSON-js/blob/master/json2.js

Existe um regexp que verifica um JSON válido, algo como:

if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

  //the json is ok

}else{

  //the json is not ok

}

EDIT : A nova versão do json2.js faz uma análise mais avançada que a anterior, mas ainda com base na substituição de uma expressão regular (a partir do comentário de @Mrchief )

Microfone
fonte
59
Isso só está verificando se o código é seguro para o eval usar. Por exemplo, a seguinte string '2011-6-27' passaria nesse teste.
SystemicPlural
4
@SystemicPlural, sim, mas a questão sobre não estava usando try / catch
Mic
8
Você não pode testar se uma sequência de caracteres é JSON válida com uma regex em JavaScript, pois as regexes JS não suportam as extensões necessárias (regexes recursivas) que permitem isso. Seu código acima falha em "{".
Venge
2
O @Mic json2.js não usa mais essa verificação simples (usa uma análise de 4 estágios para determinar JSON válido). Sugeriria revisar ou remover sua resposta. Observe que não acho que haja algo errado em "não ter um try / catch como o único mecanismo para verificar o JSON" como uma abordagem.
precisa saber é o seguinte
8
Só porque isso o ajuda, não significa que ajude o resto de nós, que, anos depois, tenha a mesma pergunta.
21716 McKay
916

Use um analisador JSON como JSON.parse:

function IsJsonString(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}
quiabo
fonte
7
Obrigado, mas acabei de executar isso com a equipe e eles querem algo que não use try / catch. A pergunta é editada junto com um novo título. Me desculpe por isso.
Chi Chan
4
@trejder: ele faz isso porque 1 não é uma string, experimentá-lo com "1"
Purefan
31
@Gumbo Meu comentário tem 1,5 anos! :] Não me lembro, o que estava fazendo há duas semanas e você me pediu para me lembrar desse projeto? :] Não, caminho ...:]
trejder
9
O problema com esta resposta é que, se a string sair, e você a analisar, você a analisará duas vezes. Em vez disso, você não poderia retornar falso em uma análise ruim, mas retornar o objeto com êxito?
Carcigenicate
5
@Carcigenicate Você poderia fazer isso. No entanto, JSON.parse("false")avalia como falso também.
Gumbo
446

Eu sei que estou 3 anos atrasado para esta pergunta, mas eu senti vontade de comentar.

Embora a solução da Gumbo funcione bem, ela não lida com alguns casos em que nenhuma exceção é levantada para JSON.parse({something that isn't JSON})

Também prefiro retornar o JSON analisado ao mesmo tempo, para que o código de chamada não precise chamar JSON.parse(jsonString) uma segunda vez.

Isso parece funcionar bem para minhas necessidades:

function tryParseJSON (jsonString){
    try {
        var o = JSON.parse(jsonString);

        // Handle non-exception-throwing cases:
        // Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
        // but... JSON.parse(null) returns null, and typeof null === "object", 
        // so we must check for that, too. Thankfully, null is falsey, so this suffices:
        if (o && typeof o === "object") {
            return o;
        }
    }
    catch (e) { }

    return false;
};
Matt H.
fonte
9
Das respostas na página, essa é a mais robusta e confiável.
Jonline
28
o && o !== nullé supérfluo.
Aleksei Matiushkin
4
O mesmo acontece com o triplo-igual a typeof, que sempre retorna uma string. :)
Hein Haraldson Berg
5
Apesar de ser um post antigo, pensei que vale a pena colocar um violino -se demonstrando a sua resposta @matth, por favor nota que os objetos não será válido .. você deve passar uma string JSON. Pode ser útil para quem está começando, eu acho.
MindVox
2
A função deve retornar undefined, não falseporque falseé uma string json válida e não há como diferenciar entre tryParseJSON("false")etryParseJSON("garbage")
sparebytes
54
// vanillaJS
function isJSON(str) {
    try {
        return (JSON.parse(str) && !!str);
    } catch (e) {
        return false;
    }
}

Uso: isJSON({}) será false, isJSON('{}')será true.

Para verificar se algo é um Arrayou Object( JSON analisado ):

// vanillaJS
function isAO(val) {
    return val instanceof Array || val instanceof Object ? true : false;
}

// ES2015
var isAO = (val) => val instanceof Array || val instanceof Object ? true : false;

Uso: isAO({}) será true, isAO('{}')será false.

moeiscool
fonte
4
Tenha cuidado, pois nullpassa nessa validação.
Farzad YZ
2
return !!(JSON.parse(str) && str);deve bloquear valores nulos. Vou atualizar a resposta com este código.
Machado
1
Esta é a melhor resposta, pois também permite verificar se o JSON foi objetivado e, portanto, não passou no parse()teste, causando WTFs.
Not2qubit
30

Aqui meu código de trabalho:

function IsJsonString(str) {
  try {
    var json = JSON.parse(str);
    return (typeof json === 'object');
  } catch (e) {
    return false;
  }
}
Anand Kumar
fonte
1
IsJsonString (nulo); // retorna verdadeiro. Ele pode ser corrigido através da comparaçãotypeof str === 'string'
gramcha
23

Eu usei um método muito simples para verificar uma string como é um JSON válido ou não.

function testJSON(text){
    if (typeof text!=="string"){
        return false;
    }
    try{
        JSON.parse(text);
        return true;
    }
    catch (error){
        return false;
    }
}

Resultado com uma sequência JSON válida:

var input='["foo","bar",{"foo":"bar"}]';
testJSON(input); // returns true;

Resultado com uma string simples;

var input='This is not a JSON string.';
testJSON(input); // returns false;

Resultado com um objeto:

var input={};
testJSON(input); // returns false;

Resultado com entrada nula:

var input=null;
testJSON(input); // returns false;

O último retorna false porque o tipo de variáveis ​​nulas é objeto.

Isso funciona sempre. :)

kukko
fonte
1
JSON.parse (null), JSON.parse ("false") não gera erros, provavelmente há mais exemplos
klodoma 15/03
Sim, você está certo, eu esqueci de verificar como a entrada é uma string ou não. Se eu fizer isso, esse método com nullinput retorna false. Mas a entrada "false" é uma sequência JSON válida. Isso será analisado boolean (false). Agora eu modifico o código para ser mais preciso.
Kukko
15

No prototypeJS, temos o método isJSON . Você pode tentar isso. Até o json pode ajudar.

"something".isJSON();
// -> false
"\"something\"".isJSON();
// -> true
"{ foo: 42 }".isJSON();
// -> false
"{ \"foo\": 42 }".isJSON();
Se eu
fonte
9
Obrigado, mas acho que usar a biblioteca de protótipos para fazer isso é um pouco exagerado.
Chi Chan
4
Você deu QUATRO exemplos, mas apenas TRÊS resultados. Qual é o resultado "{ foo: 42 }".isJSON()? Se false, como eu suponho (o resultado deve seguir a função do documento), então uma boa pergunta é: por que é falso? { foo: 42 }parece ser JSON perfeitamente válido.
Trejder
4
@trejder Infelizmente, a especificação JSON requer chaves entre aspas.
Mckmcneil 8/10/12
4
E "2002-12-15" .isJSON retorna true, enquanto JSON.parse ("2002-12-15") gera um erro.
Ychaouche 13/11
4
Acho que a melhor resposta aqui seria extrair essa função da biblioteca de protótipos e colocá-la aqui. Especialmente desde api.prototypejs.org/language/string/prototype/isjson é 404.
jcollum
5

Da String.isJSONdefinição da estrutura Prototype aqui

/**
   *  String#isJSON() -> Boolean
   *
   *  Check if the string is valid JSON by the use of regular expressions.
   *  This security method is called internally.
   *
   *  ##### Examples
   *
   *      "something".isJSON();
   *      // -> false
   *      "\"something\"".isJSON();
   *      // -> true
   *      "{ foo: 42 }".isJSON();
   *      // -> false
   *      "{ \"foo\": 42 }".isJSON();
   *      // -> true
  **/
  function isJSON() {
    var str = this;
    if (str.blank()) return false;
    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
    return (/^[\],:{}\s]*$/).test(str);
  }

então esta é a versão que pode ser usada passando um objeto string

function isJSON(str) {
    if ( /^\s*$/.test(str) ) return false;
    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
    return (/^[\],:{}\s]*$/).test(str);
  }

function isJSON(str) {
    if ( /^\s*$/.test(str) ) return false;
    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
    return (/^[\],:{}\s]*$/).test(str);
  }

console.log ("this is a json",  isJSON( "{ \"key\" : 1, \"key2@e\" : \"val\"}" ) )

console.log("this is not a json", isJSON( "{ \"key\" : 1, \"key2@e\" : pippo }" ) )

Loretoparisi
fonte
1
Alguém tem suíte de testes para comparar todas essas respostas? Eu gostaria de ver se este está correto.
Lonnie Melhor
1
Melhor ponto bom. Meus 2 centavos. Eu uso há anos na produção e sempre funcionou bem e com um tempo de execução razoável.
loretoparisi 21/11/19
4

Esta resposta para reduzir o custo da instrução trycatch.

Eu usei o JQuery para analisar cadeias JSON e usei a instrução trycatch para lidar com exceções, mas o lançamento de exceções para cadeias não analisáveis ​​diminuiu a velocidade do meu código, então usei o Regex simples para verificar a cadeia, se é uma possível cadeia JSON ou não sem pena verificando sua sintaxe, usei o modo regular analisando a string usando o JQuery:

if (typeof jsonData == 'string') {
    if (! /^[\[|\{](\s|.*|\w)*[\]|\}]$/.test(jsonData)) {
        return jsonData;
    }
}

try {
    jsonData = $.parseJSON(jsonData);
} catch (e) {

}

Embrulhei o código anterior em uma função recursiva para analisar respostas JSON aninhadas.

Rabih
fonte
O que o jQuery faz com o JSON.parse () não faz?
precisa saber é o seguinte
3

Talvez seja útil:

    function parseJson(code)
{
    try {
        return JSON.parse(code);
    } catch (e) {
        return code;
    }
}
function parseJsonJQ(code)
{
    try {
        return $.parseJSON(code);
    } catch (e) {
        return code;
    }
}

var str =  "{\"a\":1,\"b\":2,\"c\":3,\"d\":4,\"e\":5}";
alert(typeof parseJson(str));
alert(typeof parseJsonJQ(str));
var str_b  = "c";
alert(typeof parseJson(str_b));
alert(typeof parseJsonJQ(str_b));

resultado:

IE7: string , objeto, string, string

CHROME: objeto, objeto, string, string


fonte
2

Eu acho que sei por que você quer evitar isso. Mas talvez tente e pegue! == tente e pegue. ; o) Isso me veio à mente:

var json_verify = function(s){ try { JSON.parse(s); return true; } catch (e) { return false; }};

Portanto, você também pode clipe sujo para o objeto JSON, como:

JSON.verify = function(s){ try { JSON.parse(s); return true; } catch (e) { return false; }};

Como isso é o mais encapsulado possível, pode não ocorrer erro.

chrixle
fonte
2

Aqui está a versão datilografada também:

JSONTryParse(input) {
    try {
        //check if the string exists
        if (input) {
            var o = JSON.parse(input);

            //validate the result too
            if (o && o.constructor === Object) {
                return o;
            }
        }
    }
    catch (e) {
    }

    return false;
};
Mohammed Zameer
fonte
O texto datilografado não é javascript, mas sua resposta parece ser.
Lonnie Best
1

var jsonstring='[{"ConnectionString":"aaaaaa","Server":"ssssss"}]';

if(((x)=>{try{JSON.parse(x);return true;}catch(e){return false}})(jsonstring)){

document.write("valide json")

}else{
document.write("invalide json")
}

safi eddine
fonte
1

Eu deduzo do comentário de abertura que o caso de uso está descrevendo se uma resposta é HTML ou JSON. Nesse caso, quando você faz receber JSON, você provavelmente deveria ser analisá-lo e lidar com JSON inválido em algum momento de seu código de qualquer maneira. Além de tudo, imagino que você gostaria de ser informado pelo seu navegador, caso o JSON seja esperado, mas o JSON inválido seja recebido (assim como os usuários por proxy de alguma mensagem de erro significativa)!

Portanto, fazer um regex completo para JSON é desnecessário (como seria - na minha experiência - para a maioria dos casos de uso). Você provavelmente seria melhor usar algo como o abaixo:

function (someString) {
  // test string is opened with curly brace or machine bracket
  if (someString.trim().search(/^(\[|\{){1}/) > -1) {
    try { // it is, so now let's see if its valid JSON
      var myJson = JSON.parse(someString);
      // yep, we're working with valid JSON
    } catch (e) {
      // nope, we got what we thought was JSON, it isn't; let's handle it.
    }
  } else {
    // nope, we're working with non-json, no need to parse it fully
  }
}

isso evita que você precise manipular códigos não JSON válidos e cuidar do duff json ao mesmo tempo.

Jay Edwards
fonte
Essa solução híbrida parece ser uma maneira eficiente de evitar a tentativa de captura na maioria dos casos que não são JSON. Eu gosto desse aspecto da sua abordagem.
Lonnie Melhor
1
if(resp) {
    try {
        resp = $.parseJSON(resp);
        console.log(resp);
    } catch(e) {
        alert(e);
    }
}

Espero que isto funcione para você também

Darkcoder
fonte
0
function get_json(txt)
{  var data

   try     {  data = eval('('+txt+')'); }
   catch(e){  data = false;             }

   return data;
}

Se houver erros, retorne false.

Se não houver erros, retorne dados json

Emrah Tuncel
fonte
4
Na pergunta: "A solução não deve conter try / catch".
Ddmps 6/03/2013
1
Por quê? Isso é garantido ... Seria tolice desutilizar! Sinto muito por não saber inglês. Eu usei o Google Tradutor
Emrah Tuncel 6/13
Interessante. Eu gostaria de ver uma comparação de desempenho do JSON.parse versus esta solução baseada em avaliação. No entanto, isso parece assustador do ponto de vista de segurança / injeção.
Lonnie Melhor
0

Você pode usar o javascript eval () para verificar se é válida.

por exemplo

var jsonString = '{ "Id": 1, "Name": "Coke" }';
var json;

try {
  json = eval(jsonString);
} catch (exception) {
  //It's advisable to always catch an exception since eval() is a javascript executor...
  json = null;
}

if (json) {
  //this is json
}

Como alternativa, você pode usar a JSON.parsefunção do json.org :

try {
  json = JSON.parse(jsonString);
} catch (exception) {
  json = null;
}

if (json) {
  //this is json
}

Espero que isto ajude.

AVISO : eval()é perigoso se alguém adicionar código JS malicioso, pois ele será executado. Verifique se a string JSON é confiável , ou seja, de uma fonte confiável.

Editar Para minha primeira solução, é recomendável fazer isso.

 try {
      json = eval("{" + jsonString + "}");
    } catch (exception) {
      //It's advisable to always catch an exception since eval() is a javascript executor...
      json = null;
    }

Para garantir json-ness. Se o jsonStringJSON não for puro, o eval lançará uma exceção.

Buhake Sindi
fonte
O primeiro exemplo de eval diz que "<div> foo </div>" é JSON válido. Pode funcionar de maneira diferente em navegadores diferentes, mas parece que no FireFox, eval () aceita XML.
Mark-Lutton-
Obrigado, mas acabei de executar isso com a equipe e eles querem algo que não use try / catch. A pergunta é editada junto com um novo título. Me desculpe por isso.
Chi Chan
@ Mark Lutton, o tipo de objeto não será do JSON, mas do XML Dom Document (eu esqueci qual é o tipo exato no firefox).
Buhake Sindi 14/09/10
1
eval também aceita JavaScript válido, como "alert (5);" e cadeias de caracteres entre aspas simples, que não são JSON válidas.
Mark-Lutton-
12
Isso é pura avaliação.
Chris Baker
0

Ah, você definitivamente pode usar o try catch para verificar se é ou não um JSON válido

Testado no Firfox Quantom 60.0.1

use function dentro de uma função para testar o JSON e use essa saída para validar a sequência. ouve um exemplo.

    function myfunction(text){

       //function for validating json string
        function testJSON(text){
            try{
                if (typeof text!=="string"){
                    return false;
                }else{
                    JSON.parse(text);
                    return true;                            
                }
            }
            catch (error){
                return false;
            }
        }

  //content of your real function   
        if(testJSON(text)){
            console.log("json");
        }else{
            console.log("not json");
        }
    }

//use it as a normal function
        myfunction('{"name":"kasun","age":10}')
Aylian Craspa
fonte
0

A função IsJsonString(str)que está sendo usada JSON.parse(str)não funciona no meu caso.
Eu tentei validar a saída json do GraphiQL, ela sempre retorna falsa. Sorte minha, isJSON funciona melhor:

var test = false;

$('body').on('DOMSubtreeModified', '.resultWrap', function() {

    if (!test) {   
        var resultWrap = "{" + $('#graphiql .resultWrap').text().split("{").pop();
        if isJSON(resultWrap) {test = !test;}
        console.log(resultWrap); 
        console.log(resultWrap.isJSON());
    }

});

Saída de amostra:

THREE.WebGLRenderer 79
draw.js:170 {xxxxxxxxxx
draw.js:170 false
draw.js:170 {xxxxxxxxxx 
draw.js:170 false
draw.js:170 {xxxxxxxxxx 
draw.js:170 false
draw.js:170 {xxxxxxxxxx 
draw.js:170 false
draw.js:170 {​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,  "tick": 156,​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,  "tick": 156,  "tickr": 1.56,​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,  "tick": 156,  "tickr": 1.56,  "fps": 41.666666666666664,​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,  "tick": 156,  "tickr": 1.56,  "fps": 41.666666666666664,  "width": 396.984,​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,  "tick": 156,  "tickr": 1.56,  "fps": 41.666666666666664,  "width": 396.984,  "height": 327
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,  "tick": 156,  "tickr": 1.56,  "fps": 41.666666666666664,  "width": 396.984,  "height": 327}​
draw.js:170 false
draw.js:170 {  "PI": 3.141592653589793,  "time": 1570751209006,  "tick": 156,  "tickr": 1.56,  "fps": 41.666666666666664,  "width": 396.984,  "height": 327}
draw.js:170 true
Chetabahana
fonte
0

Para pessoas que gostam da convenção .Net de funções "try" que retornam um booleano e manipulam um parâmetro byref contendo o resultado. Se você não precisar do parâmetro out, poderá omiti-lo e apenas usar o valor de retorno.

StringTests.js

  var obj1 = {};
  var bool1 = '{"h":"happy"}'.tryParse(obj1); // false
  var obj2 = {};
  var bool2 = '2114509 GOODLUCKBUDDY 315852'.tryParse(obj2);  // false

  var obj3 = {};
  if('{"house_number":"1","road":"Mauchly","city":"Irvine","county":"Orange County","state":"California","postcode":"92618","country":"United States of America","country_code":"us"}'.tryParse(obj3))
    console.log(obj3);

StringUtils.js

String.prototype.tryParse = function(jsonObject) {
  jsonObject = jsonObject || {};
  try {
    if(!/^[\[{]/.test(this) || !/[}\]]$/.test(this)) // begin / end with [] or {}
      return false; // avoid error handling for strings that obviously aren't json
    var json = JSON.parse(this);
    if(typeof json === 'object'){
      jsonObject.merge(json);
      return true;
    }
  } catch (e) {
    return false;
  }
}

ObjectUtils.js

Object.defineProperty(Object.prototype, 'merge', {
  value: function(mergeObj){
    for (var propertyName in mergeObj) {
      if (mergeObj.hasOwnProperty(propertyName)) {
        this[propertyName] = mergeObj[propertyName];
      }      
    }
    return this;
  },
  enumerable: false, // this is actually the default
});
toddmo
fonte
-2

Código de uma linha muito simples (mas abordagem Hacky)

if (expected_json.id === undefined){
   // not a json
}
else{
   // json
}

NOTA: Isso só funciona se você estiver esperando algo com uma string JSON como id. Estou usando-o para uma API e esperando o resultado em JSON ou em alguma string de erro.

Abhishek Goel
fonte