Cómo verificar si una cadena es una cadena JSON válida en JavaScript sin 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>")

La solución no debe contener try / catch. Algunos de nosotros activamos "romper todos los errores" y no les gusta que el depurador rompa esas cadenas JSON no válidas.

Chi Chan
fuente
25
¿Hay alguna razón válida para no usar try?
Nick T
77
@NickT Porque si activa "romper todos los errores" en el depurador, lo hará. Chrome ahora tiene la opción de romper los errores no detectados.
Chi Chan
66
Use solo 2 líneas para verificarlo con try catch. var isValidJSON = verdadero; pruebe {JSON.parse (jsonString)} catch {isValidJSON = false; }
efkan
18
Si bien eso funciona, es terriblemente grosero y una mala práctica. Try / catch está diseñado para un comportamiento excepcional y manejo de errores, no para el flujo general del programa.
Tasgall
77
@Tasgall Como regla general, sí. Pero, ¿qué hacer si el enfoque try / catch es más eficaz que cualquier enfoque basado en validación? ¿Ir con la opción (a veces significativamente) más lenta solo porque la alternativa es "mala práctica"? No hay nada funcionalmente malo con el método try / catch, por lo que no hay razón para no usarlo. Es importante que los nuevos programadores desarrollen buenos estándares de codificación, pero es igualmente importante no reforzar el cumplimiento ciego de las pautas convencionales, especialmente en los casos en que las pautas hacen las cosas más difíciles de lo que deberían ser.
Abion47

Respuestas:

172

Un comentario primero. La pregunta era sobre no usar try/catch.
Si no le importa usarlo, lea la respuesta a continuación. Aquí solo verificamos una JSONcadena usando una expresión regular, y funcionará en la mayoría de los casos, no en todos los casos.

Eche un vistazo a la línea 450 en https://github.com/douglascrockford/JSON-js/blob/master/json2.js

Hay una expresión regular que comprueba un JSON válido, algo así 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

}

EDITAR : La nueva versión de json2.js hace un análisis más avanzado que el anterior, pero aún se basa en un reemplazo de expresiones regulares (del comentario de @Mrchief )

Micrófono
fuente
59
Esto solo verifica si el código es seguro para que eval lo use. Por ejemplo, la siguiente cadena '2011-6-27' pasaría esa prueba.
SystemicPlural
44
@SystemicPlural, sí, pero la pregunta era sobre no usar try / catch
Mic
8
No puede probar si una cadena es JSON válida con una expresión regular en JavaScript, ya que las expresiones regulares JS no admiten las extensiones necesarias (expresiones regulares recursivas) que le permiten hacerlo. Su código anterior falla en "{".
Venge
2
@Mic json2.js ya no usa esa verificación simple (en su lugar usa un análisis de 4 etapas para determinar un JSON válido). Sugeriría revisar o eliminar su respuesta. Tenga en cuenta que no creo que haya ningún problema con "no tener un try / catch como único mecanismo para verificar JSON" como enfoque.
Mrchief
8
El hecho de que lo ayude no significa que nos ayude al resto de nosotros, quienes, años después, tenemos la misma pregunta.
McKay
917

Use un analizador JSON como JSON.parse:

function IsJsonString(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}
Gumbo
fuente
77
Gracias, pero acabo de ejecutar esto con el equipo y quieren algo que no use try / catch. La pregunta se edita junto con un nuevo título. Lo siento por eso.
Chi Chan
44
@trejder: lo hace porque 1 no es una cadena, pruébalo con "1"
Purefan
31
@Gumbo ¡Mi comentario tiene 1,5 años! :] No recuerdo, ¿qué estaba haciendo hace dos semanas y me pediste que recordara ese proyecto? :] No, camino ...:]
trejder
99
El problema con esta respuesta es que si la cadena se desprotege y la analiza, la habrá analizado dos veces. ¿No podría devolver falso en un análisis incorrecto, pero devolver el objeto con éxito?
Carcigenicate
55
@Carcigenicate Podrías hacer eso. Sin embargo, también se JSON.parse("false")evalúa como falso .
Gumbo
446

Sé que llego 3 años tarde a esta pregunta, pero sentí ganas de hablar.

Si bien la solución de Gumbo funciona muy bien, no maneja algunos casos en los que no se plantea ninguna excepción para JSON.parse({something that isn't JSON})

También prefiero devolver el JSON analizado al mismo tiempo, para que el código de llamada no tenga que llamar JSON.parse(jsonString) por segunda vez.

Esto parece funcionar bien para mis necesidades:

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.
fuente
99
De las respuestas en la página, esta es la más sólida y confiable.
Jonline
28
o && o !== nulles superfluo
Aleksei Matiushkin
44
Entonces, está usando triples iguales con typeof, que siempre devuelve una cadena. :)
Hein Haraldson Berg
55
A pesar de ser una publicación anterior, pensé que valía la pena poner un violín demostrando tu respuesta @matth, ten en cuenta que los objetos no serán válidos ... debes pasar una cadena JSON. Supongo que puede ser útil para cualquiera que esté comenzando.
MindVox
2
La función debería volver undefined, no falseporque falsesea ​​una cadena json válida y no haya forma de diferenciar entre tryParseJSON("false")ytryParseJSON("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 si algo es un Arrayo Object( JSON analizado ):

// 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
fuente
44
Tenga cuidado ya que nullpasa esta validación.
Farzad YZ
2
return !!(JSON.parse(str) && str);debería bloquear valores nulos. Actualizaré la respuesta con este código.
Machado
1
Esta es la mejor respuesta, ya que también le permite verificar si el JSON ha sido objetivado y, por lo tanto, no pasa la parse()prueba, causando WTF.
not2qubit
30

Aquí mi código de trabajo:

function IsJsonString(str) {
  try {
    var json = JSON.parse(str);
    return (typeof json === 'object');
  } catch (e) {
    return false;
  }
}
Anand Kumar
fuente
1
IsJsonString (nulo); // devuelve verdadero. Se puede solucionar comparandotypeof str === 'string'
gramcha
23

Utilicé un método realmente simple para verificar si una cadena es un JSON válido o no.

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

Resultado con una cadena JSON válida:

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

Resultado con una cadena simple;

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

Resultado con un objeto:

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

Resultado con entrada nula:

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

El último devuelve falso porque el tipo de variables nulas es objeto.

Esto funciona todo el tiempo. :)

kukko
fuente
1
JSON.parse (nulo), JSON.parse ("falso") no arroja errores, probablemente hay más ejemplos
klodoma
Sí, tienes razón, olvidé comprobar cómo la entrada es una cadena o no. Si hago eso, este método con nullentrada devuelve falso. Pero la entrada "falsa" es una cadena JSON válida. Esto será analizado a boolean (false). Ahora modifico el código para que sea más preciso.
kukko
15

En prototypeJS, tenemos el método isJSON . Puedes probar eso. Incluso json podría ayudar.

"something".isJSON();
// -> false
"\"something\"".isJSON();
// -> true
"{ foo: 42 }".isJSON();
// -> false
"{ \"foo\": 42 }".isJSON();
Si yo
fuente
99
Gracias, pero creo que usar la biblioteca prototipo para hacer esto es un poco exagerado.
Chi Chan
44
Diste CUATRO ejemplos pero solo TRES resultados. ¿Para qué es el resultado "{ foo: 42 }".isJSON()? Si false, como supongo (el resultado debe seguir a la función del documento), entonces una buena pregunta es, ¿por qué es falso? { foo: 42 }parece ser perfectamente válido JSON.
trejder
44
@trejder Desafortunadamente, la especificación JSON requiere claves entre comillas.
mikermcneil
44
Y "2002-12-15" .isJSON devuelve verdadero, mientras que JSON.parse ("2002-12-15") arroja un error.
Ychaouche
44
Creo que la mejor respuesta aquí sería sacar esa función de la biblioteca de prototipos y colocarla aquí. Especialmente desde que api.prototypejs.org/language/string/prototype/isjson es 404.
jcollum
5

De la String.isJSONdefinición del marco prototipo aquí

/**
   *  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);
  }

así que esta es la versión que se puede usar pasando un objeto de cadena

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
fuente
1
¿Alguien tiene un conjunto de pruebas para comparar todas estas respuestas? Me gustaría ver si este es correcto.
Lonnie Best el
1
@LonnieBest buen punto. Mis 2 centavos Lo he usado durante años en producción y siempre ha funcionado bien y con un tiempo de ejecución razonable.
loretoparisi
4

Esta respuesta para reducir el costo de la declaración trycatch.

Usé JQuery para analizar cadenas JSON y usé la declaración trycatch para manejar excepciones, pero lanzar excepciones para cadenas no analizables ralentizó mi código, por lo que usé Regex simple para verificar la cadena si es una posible cadena JSON o no sin pluma comprobando su sintaxis, luego usé la forma regular al analizar la cadena usando JQuery:

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

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

}

Envolví el código anterior en una función recursiva para analizar respuestas JSON anidadas.

Rabih
fuente
¿Qué hace jQuery que JSON.parse () no hace?
ADJenks
3

Tal vez sea ú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));

salida:

IE7: cadena , objeto, cadena, cadena

CROMO: objeto, objeto, cadena, cadena


fuente
2

Creo que sé por qué quieres evitar eso. Pero tal vez intente y atrape! == intente y atrape. ; o) Esto vino a mi mente:

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

Por lo tanto, también puede hacer un clip sucio al objeto JSON, como:

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

Como esto está tan encapsulado como sea posible, es posible que no se rompa por error.

chrixle
fuente
2

Aquí está la versión mecanografiada también:

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
fuente
El mecanografiado no es javascript, pero su respuesta parece serlo.
Lonnie Best el
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
fuente
1

Por el comentario inicial deduzco que el caso de uso está delineando si una respuesta es HTML o JSON. En cuyo caso, cuando lo hagas recibir JSON, probablemente debería ser el análisis y la manipulación de JSON no válida en algún momento de su código de todos modos. Aparte de cualquier cosa, ¡imagino que le gustaría que su navegador le informara si se espera JSON pero se recibe JSON no válido (al igual que los usuarios por proxy de algún mensaje de error significativo)!

Por lo tanto, hacer una regex completa para JSON es innecesario (como sería, en mi experiencia, para la mayoría de los casos de uso). Probablemente sería mejor usar algo como lo siguiente:

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

eso debería evitar que tenga que manejar un código no JSON válido y cuidar de duff json al mismo tiempo.

Jay Edwards
fuente
Parece que esta solución híbrida sería una forma eficiente de evitar tener que hacer un intento de captura en la mayoría de los casos que no son JSON. Me gusta ese aspecto de tu enfoque.
Lonnie Best el
1
if(resp) {
    try {
        resp = $.parseJSON(resp);
        console.log(resp);
    } catch(e) {
        alert(e);
    }
}

Espero que esto funcione para usted también

Darkcoder
fuente
0
function get_json(txt)
{  var data

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

   return data;
}

Si hay errores, devuelva falso.

Si no hay errores, devuelva datos json

Emrah Tuncel
fuente
44
En la pregunta: "La solución no debe contener try / catch".
ddmps
1
¿Por qué? Esta es una forma garantizada ... ¡Sería una tontería dejar de usar! Perdón por no saber inglés.
Usé
Interesante. Me gustaría ver una comparación de rendimiento de JSON.parse versus esta solución basada en evaluación. Sin embargo, esto parece aterrador desde una perspectiva de seguridad / inyección.
Lonnie Best el
0

Puede usar la función de evaluación de JavaScript () para verificar si es válida.

p.ej

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
}

Alternativamente, puede usar la JSON.parsefunción de json.org :

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

if (json) {
  //this is json
}

Espero que esto ayude.

ADVERTENCIA : eval()es peligroso si alguien agrega código JS malicioso, ya que lo ejecutará. Asegúrese de que la cadena JSON sea confiable , es decir, la obtuvo de una fuente confiable.

Editar Para mi primera solución, se recomienda hacer esto.

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

Para garantizar json-ness. Si el jsonStringJSON no es puro, la evaluación arrojará una excepción.

Buhake Sindi
fuente
El primer ejemplo que usa eval dice que "<div> foo </div>" es JSON válido. Puede funcionar de manera diferente en diferentes navegadores, pero parece que en FireFox, eval () acepta XML.
Mark Lutton
Gracias, pero acabo de ejecutar esto con el equipo y quieren algo que no use try / catch. La pregunta se edita junto con un nuevo título. Lo siento por eso.
Chi Chan
@ Mark Lutton, el tipo de objeto no será de JSON sino de XML Dom Document (olvidé cuál es el tipo exacto en firefox).
Buhake Sindi
1
eval también acepta JavaScript válido, como "alert (5);" y cadenas entre comillas simples, que no son JSON válidas.
Mark Lutton
12
Esto es pura evaluación.
Chris Baker
0

Oh, definitivamente puedes usar try catch para verificar si es o no un JSON válido

Probado en Firfox Quantom 60.0.1

use la función dentro de una función para hacer que se pruebe el JSON y use esa salida para validar la cadena. escucha un ejemplo.

    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
fuente
0

La función IsJsonString(str), que está usando JSON.parse(str), no funciona en mi caso.
Intenté validar la salida json de GraphiQL , siempre devuelve falso. Por suerte, isJSON funciona mejor:

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());
    }

});

Salida de muestra:

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
fuente
0

Para las personas a las que les gusta la convención .Net de funciones "probar" que devuelven un valor booleano y manejan un parámetro byref que contiene el resultado. Si no necesita el parámetro out, puede omitirlo y simplemente usar el 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
fuente
-2

Código de línea simple muy simple (pero enfoque de Hacky)

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

NOTA: Esto solo funciona si espera que algo sea una cadena JSON como id. Lo estoy usando para una API y espero el resultado en JSON o en alguna cadena de error.

Abhishek Goel
fuente