Como testar se uma string é JSON ou não?


191

Eu tenho uma chamada AJAX simples e o servidor retornará uma sequência JSON com dados úteis ou uma sequência de mensagens de erro produzida pela função PHP mysql_error(). Como posso testar se esses dados são uma string JSON ou a mensagem de erro.

Seria bom usar uma função chamada, isJSONassim como você pode usá-la instanceofpara testar se algo é uma matriz.

É isso que eu quero:

if (isJSON(data)){
    //do some data stuff
}else{
    //report the error
    alert(data);
}

Talvez usando eval()se ele retornar undefined, então, não é JSON
MatuDuke 21/03

4

2
Obrigado a todos, desculpe, eu não encontrei esse outro post antes.
21412

1
Tecnicamente, este não é um idiota do 3710204, uma vez que se pergunta se é json válido, que é uma barra muito mais alta do que é json.
Carlin.scott

Respostas:


320

Use JSON.parse

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}

53
O tratamento de exceções não deve ser usado para fazer o esperado.
LuisZavaleta

44
JSON.parse(1234)OU JSON.parse(0)OU JSON.parse(false)OU JSON.parse(null)todos não gerarão Exceção e retornarão verdadeiro !!. não use esta resposta
Zalaboza

19
@Zalaboza 1234, 0, false, e nullsão todos valores JSON válido. Se você deseja um predicado que testa se o JSON representa um objeto, precisará fazer um pouco mais.
Michael Lang

20
JSON.parsefaz muita computação para analisar a string e fornecer o objeto json, se for bem-sucedido, mas você está descartando o resultado que alguns usuários podem querer usar. Isso não parece ser bom. Eu preferiria return {value: JSON.parse(str), valid: true};e no bloco catch return {value: str, valid: false};... e mudaria o nome da função para tryParse().
Nawaz

7
@luisZavaleta então o que você sugere como um método
PirateApp

80

Este código é JSON.parse(1234)ou JSON.parse(0)ou JSON.parse(false)ou JSON.parse(null)todos retornarão true.

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}

Então, eu reescrevi o código desta maneira:

function isJson(item) {
    item = typeof item !== "string"
        ? JSON.stringify(item)
        : item;

    try {
        item = JSON.parse(item);
    } catch (e) {
        return false;
    }

    if (typeof item === "object" && item !== null) {
        return true;
    }

    return false;
}

Resultado do teste:

resultado do teste isJson


4
Bom trabalho! Sua última declaração se poderia ser simplificado para uma instrução de retorno simples, tais como:return (typeof suspect === "object" && suspect !== null);
Nebulosar

38

Vamos recapitular isso (para 2019+).

Argumento : Valores tais como true, false, nullsão válidos JSON (?)

FATO : Esses valores primitivos são analisáveis ​​por JSON, mas não são estruturas JSON bem formadas . A especificação JSON indica que o JSON se baseia em duas estruturas: Uma coleção de pares nome / valor (objeto) ou uma lista ordenada de valores (matriz).

Argumento : O tratamento de exceções não deve ser usado para fazer o esperado.
(Este é um comentário com mais de 25 votos positivos!)

FATO : Não! Definitivamente, é legal usar try / catch, especialmente em um caso como este. Caso contrário, você precisará fazer muitas coisas de análise de string, como operações de tokenizing / regex; o que teria um desempenho terrível.

hasJsonStructure()

Isso é útil se seu objetivo é verificar se alguns dados / texto possuem o formato de intercâmbio JSON adequado.

function hasJsonStructure(str) {
    if (typeof str !== 'string') return false;
    try {
        const result = JSON.parse(str);
        const type = Object.prototype.toString.call(result);
        return type === '[object Object]' 
            || type === '[object Array]';
    } catch (err) {
        return false;
    }
}

Uso:

hasJsonStructure('true')             // —» false
hasJsonStructure('{"x":true}')       // —» true
hasJsonStructure('[1, false, null]') // —» true

safeJsonParse()

E isso é útil se você quiser ter cuidado ao analisar alguns dados para um valor JavaScript.

function safeJsonParse(str) {
    try {
        return [null, JSON.parse(str)];
    } catch (err) {
        return [err];
    }
}

Uso:

const [err, result] = safeJsonParse('[Invalid JSON}');
if (err) {
    console.log('Failed to parse JSON: ' + err.message);
} else {
    console.log(result);
}

1
O link para o JSON Spec diz o seguinte: "Um texto JSON é uma sequência de tokens formados a partir de pontos de código Unicode que estão em conformidade com a gramática de valor JSON". e "Um valor JSON pode ser um objeto, matriz, número, sequência, verdadeiro, falso ou nulo". - Como você chegou à conclusão de que um JSON só pode ser objeto ou matriz no nível raiz? Eu não posso ver isso no spec, nem qualquer coisa a respeito "estruturas JSON bem formadas"
Relequestual

Leia o segundo parágrafo que começa com "JSON é construído em duas estruturas ..." @ json.org ou parágrafos 4 e 5 do ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
Onur Yıldırım

O json.org é apenas informativo. A leitura das especificações às quais você vinculou não suporta sua sugestão. A especificação menciona a RFC 8259 como a mais recente RFC. Veja exemplos de JSON texs válidos que contêm apenas valores tools.ietf.org/html/rfc8259#section-13 - O RFC 8259 foi projetado para resolver possíveis ambiguidades e confusões, assim.
Relequestual

Leia a resposta novamente. Estou dizendo que valores como primitivos (ou seja, valores de texto nos exemplos RFC) não são "estruturas" JSON. Não há ambiguidades. Você pode analisá-los como JSON, é válido fazê-lo. Mas eles não são dados estruturados. O JSON é inventado principalmente como um formato de intercâmbio »usado para dados estruturados» que podem ser um objeto ou matriz.
Onur Yıldırım

1
OK, então acho que concordamos. Os primaativos são JSON válidos de acordo com a especificação, mas não são "estruturas". Isso é bom. Mas você disse "Argumento: valores como verdadeiro, falso, nulo são JSON (?) Válidos. Fato: Sim e não!" - O fato é que o JSON da ARE é válido de acordo com a especificação. Opiniões sobre se são úteis ou não são irrelevantes para esse fato.
Relequestual

20

Se o servidor estiver respondendo com JSON, ele terá um application/jsontipo de conteúdo; se estiver respondendo com uma mensagem de texto sem formatação, deverá ter um text/plaintipo de conteúdo. Verifique se o servidor está respondendo com o tipo de conteúdo correto e teste-o.


4
Isso está errado, existem muitos outros tipos de mídia compatíveis com o json. Além disso, overrideMimeTypepode substituir o cabeçalho do tipo de conteúdo.
Knu

13

ao usar jQuery $.ajax()a resposta terá a responseJSONpropriedade se a resposta for JSON, isso pode ser verificado assim:

if (xhr.hasOwnProperty('responseJSON')) {}

2
Esta é suspeito é realmente a resposta que a maioria das pessoas estão procurando, provavelmente até o OP
Kirby

Isso é muito mais elegante do que usar o bloco try catch
Anurag Sinha

6

Eu gosto da melhor resposta, mas se for uma string vazia, ela retornará true. Então, aqui está uma correção:

function isJSON(MyTestStr){
    try {
        var MyJSON = JSON.stringify(MyTestStr);
        var json = JSON.parse(MyJSON);
        if(typeof(MyTestStr) == 'string')
            if(MyTestStr.length == 0)
                return false;
    }
    catch(e){
        return false;
    }
    return true;
}

var json não é usado? ou apenas para pegar o erro?
stackdave

5
var parsedData;

try {
    parsedData = JSON.parse(data)
} catch (e) {
    // is not a valid JSON string
}

No entanto, sugerirei que sua chamada / serviço http sempre retorne dados no mesmo formato. Portanto, se você tiver um erro, deverá ter um objeto JSON que envolva esse erro:

{"error" : { "code" : 123, "message" : "Foo not supported" } } 

E talvez use, além do status HTTP, um código 5xx.


5

Bem ... Depende da maneira como você está recebendo seus dados. Eu acho que o servidor está respondendo com uma string formatada em JSON (usando json_encode () no PHP, por exemplo). Se você estiver usando a postagem do JQuery e definir os dados de resposta para um formato JSON e for um JSON malformado, isso produzirá um erro:

$.ajax({
  type: 'POST',
  url: 'test2.php',
  data: "data",
  success: function (response){

        //Supposing x is a JSON property...
        alert(response.x);

  },
  dataType: 'json',
  //Invalid JSON
  error: function (){ alert("error!"); }
});

Mas, se você estiver usando a resposta de tipo como texto, precisará usar $ .parseJSON. De acordo com o site jquery: "A transmissão de uma string JSON malformada pode resultar no lançamento de uma exceção". Assim, seu código será:

$.ajax({
  type: 'POST',
  url: 'test2.php',
  data: "data",
  success: function (response){

        try {
            parsedData = JSON.parse(response);
        } catch (e) {
            // is not a valid JSON string
        }

  },
  dataType: 'text',
});

A menos, claro que você está tentando analisar o texto de erro na função de erro no exemplo acima e não tenho certeza se é JSON ...
Kirby

Ótima resposta, embora responseesteja vazia, ela irá para success: '(
Henrik Petterson 24/07

4

Provavelmente, existem testes que você pode fazer, por exemplo, se você sabe que o JSON retornado sempre será cercado {e, em }seguida, você pode testar esses caracteres ou algum outro método hacky. Ou você pode usar a biblioteca JS json.org para tentar analisá-la e testar se ela foi bem-sucedida.

No entanto, eu sugeriria uma abordagem diferente. Seu script PHP atualmente retorna JSON se a chamada for bem-sucedida, mas outra coisa se não for. Porque nem sempre retornar JSON?

Por exemplo

Chamada bem sucedida:

{ "status": "success", "data": [ <your data here> ] }

Chamada incorreta:

{ "status": "error", "error": "Database not found" }

Isso tornaria muito mais fácil escrever o JS do lado do cliente - tudo o que você precisa fazer é verificar o membro "status" e agir de acordo.


4

Eu uso apenas 2 linhas para fazer isso:

var isValidJSON = true;
try { JSON.parse(jsonString) } catch { isValidJSON = false }

Isso é tudo!

Mas lembre-se de que existem 2 traps:
1. JSON.parse(null)retorna null
2. Qualquer número ou string pode ser analisado com o JSON.parse()método.
   JSON.parse("5")retorna 5
   JSON.parse(5)retorna5

Vamos jogar alguns códigos:

// TEST 1
var data = '{ "a": 1 }'

// Avoiding 'null' trap! Null is confirmed as JSON.
var isValidJSON = data ? true : false
try { JSON.parse(data) } catch(e) { isValidJSON = false }

console.log("data isValidJSON: ", isValidJSON);
console.log("data isJSONArray: ", isValidJSON && JSON.parse(data).length ? true : false);

Console outputs:
data isValidJSON:  true
data isJSONArray:  false


// TEST 2
var data2 = '[{ "b": 2 }]'

var isValidJSON = data ? true : false
try { JSON.parse(data2) } catch(e) { isValidJSON = false }

console.log("data2 isValidJSON: ", isValidJSON);
console.log("data2 isJSONArray: ", isValidJSON && JSON.parse(data2).length ? true : false);

Console outputs:
data2 isValidJSON:  true
data2 isJSONArray:  true


// TEST 3
var data3 = '[{ 2 }]'

var isValidJSON = data ? true : false
try { JSON.parse(data3) } catch(e) { isValidJSON = false }

console.log("data3 isValidJSON: ", isValidJSON);
console.log("data3 isJSONArray: ", isValidJSON && JSON.parse(data3).length ? true : false);

Console outputs:
data3 isValidJSON:  false
data3 isJSONArray:  false


// TEST 4
var data4 = '2'

var isValidJSON = data ? true : false
try { JSON.parse(data4) } catch(e) { isValidJSON = false }

console.log("data4 isValidJSON: ", isValidJSON);
console.log("data4 isJSONArray: ", isValidJSON && JSON.parse(data4).length ? true : false);


Console outputs:
data4 isValidJSON:  true
data4 isJSONArray:  false


// TEST 5
var data5 = ''

var isValidJSON = data ? true : false
try { JSON.parse(data5) } catch(e) { isValidJSON = false }

console.log("data5 isValidJSON: ", isValidJSON);
console.log("data5 isJSONArray: ", isValidJSON && JSON.parse(data5).length ? true : false);


Console outputs:
data5 isValidJSON:  false
data5 isJSONArray:  false

// TEST 6
var data6; // undefined

var isValidJSON = data ? true : false
try { JSON.parse(data6) } catch(e) { isValidJSON = false }

console.log("data6 isValidJSON: ", isValidJSON);
console.log("data6 isJSONArray: ", isValidJSON && JSON.parse(data6).length ? true : false);

Console outputs:
data6 isValidJSON:  false
data6 isJSONArray:  false

Eu criei um violino para esta resposta em jsfiddle.net/fatmonk/gpn4eyav, que inclui a opção de adicionar também seus próprios dados de teste do usuário. Isso me parece a base de uma boa função de biblioteca, mas eu gostaria de entender mais sobre por que o Teste 1 não é uma matriz JSON válida.
Fat Monk

Porque uma matriz deve ser especificada usando [e ]. Por exemplo, [1, 2, 3]é uma matriz numérica. ["a", "b", "c"]é uma matriz de cadeias de caracteres. E [{"a":1}, {"b":2}]é uma matriz JSON. Seu trabalho com o jsfiddle parece realmente útil!
efkan

Tão simples como isso?! Portanto, o Teste 1 é um objeto JSON e o Teste 2 é uma matriz JSON que consiste em um único elemento de objeto JSON. Eu entendi isso corretamente?
Fat Monk

A questão sinalizada como uma possível duplicata disso ( stackoverflow.com/questions/3710204/… ) pergunta sobre como conseguir isso sem usar try / catch, então bifurquei meu violino para tentar alcançar esse objetivo também. O fork está em jsfiddle.net/fatmonk/827jsuvr e funciona com todos os testes acima, exceto no Teste 3, que apresenta erros no JSON.parse. Alguém pode aconselhar como evitar esse erro sem usar try?
Fat Monk

Seu jsfiddleaplicativo gera um erro porque o Teste 3 não possui uma expressão JSON válida. Portanto, um try-catchdeve ser usado para capturar esse erro e avaliar qualquer erro, pois a expressão não é JSON ao analisar como o Teste 3 acima:try { JSON.parse(data3) } catch(e) { isValidJSON = false }
efkan

2

Você pode tentar decodificá-lo e capturar a exceção (nativo ou json2.js ):

try {
  newObj = JSON.parse(myJsonString);
} catch (e) {
  console.log('Not JSON');
}

No entanto, sugiro que a resposta seja sempre JSON válida. Se você receber um erro de volta da sua consulta MySQL, basta enviar de volta o JSON com o erro:

{"error":"The MySQL error string."}

E depois:

if (myParsedJSON.error) {
  console.log('An error occurred: ' + myParsedJSON.error);
}

2

Aviso: Para métodos baseados em JSON.parse- matrizes e strings entre aspas também passarão (isto é console.log(JSON.parse('[3]'), JSON.parse('"\uD800"'))) .

Para evitar todas as primitivas JSON que não são objetos (booleano, nulo, matriz, número, cadeia de caracteres), sugiro o seguinte:

/* Validate a possible object ie. o = { "a": 2 } */
const isJSONObject = (o) => 
  !!o && (typeof o === 'object') && !Array.isArray(o) && 
  (() => { try { return Boolean(JSON.stringify(o)); } catch { return false } })()

/* Validate a possible JSON object represented as string ie. s = '{ "a": 3 }' */
function isJSONObjectString(s) {
    try {
        const o = JSON.parse(s);
        return !!o && (typeof o === 'object') && !Array.isArray(o)
    } catch {
        return false
    }
}

Código Explicação

  • !! o - Não é falso (exclui null, que é registrado como tipo de 'objeto')
  • (typeof o === 'object') - Exclui booleano, número e string
  • ! Array.isArray (o) - Excluir matrizes (que são registradas como tipo de 'objeto')
  • try ... JSON.stringify / JSON.parse - Solicita ao mecanismo JavaScript para determinar se JSON válido

Por que não usar a resposta hasJsonStructure ()?

Confiar toString()não é uma boa ideia. Isso ocorre porque diferentes mecanismos JavaScript podem retornar uma representação de sequência diferente. Em geral, os métodos que dependem disso podem falhar em ambientes diferentes ou podem estar sujeitos a falhas posteriormente, caso o mecanismo altere o resultado da string

Por que pegar uma exceção não é um hack?

Foi levantado que capturar uma exceção para determinar a validade de algo nunca é o caminho certo a seguir. Geralmente, esse é um bom conselho, mas nem sempre. Nesse caso, é provável que a captura de exceções seja a melhor rota, pois depende da implementação do mecanismo JavaScript de validar dados JSON.

Confiar no mecanismo JS oferece as seguintes vantagens:

  1. Mais completo e atualizado à medida que as especificações JSON mudam
  2. É provável que corra mais rápido (como é o código de nível inferior)

Quando tiver a oportunidade de usar o mecanismo JavaScript, sugiro fazê-lo. Particularmente nesse caso. Embora possa parecer hacky capturar uma exceção, você está realmente apenas lidando com dois possíveis estados de retorno de um método externo.


1

Aqui está um código com algumas pequenas modificações na resposta de Bourne. Como JSON.parse (number) funciona bem sem nenhuma exceção, o isNaN adicionado.

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return isNaN(str);
}

0

Todas as strings json começam com '{' ou '[' e terminam com o correspondente '}' ou ']', portanto, verifique isso.

Veja como o Angular.js faz isso:

var JSON_START = /^\[|^\{(?!\{)/;
var JSON_ENDS = {
  '[': /]$/,
  '{': /}$/
};

function isJsonLike(str) {
    var jsonStart = str.match(JSON_START);
    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
}

https://github.com/angular/angular.js/blob/v1.6.x/src/ng/http.js


@DukeDougal quer esclarecer? Às vezes, as pessoas começam o json com um '[', mas isso não é muito comum.
Carlin.scott

1
Você precisa analisá-lo para descobrir se é JSON válido. Se for JSON inválido, não será JSON. A questão é "como saber se uma string é JSON ou não?". Pela sua abordagem, isso seria JSON {fibble - e realmente não é JSON. Considere também casos como o número 1 por si só - que é JSON válido.
Duke Dougal

1
"Se é JSON inválido, não é JSON". O fato de você precisar usar a palavra "válido" mostra que você está adicionando uma qualificação ao fato de ser mais do que apenas json. A questão era simplesmente "é json" e meu exemplo de código responde perfeitamente a essa pergunta sem assumir requisitos adicionais.
Carlin.scott 9/06/16

má idéia se você estiver usando alguns dos sistemas de gabaritos e tiver algo como { someValue }passará automaticamente na validação.
Ncubica 6/09/16

@ ncubica, então você está usando um modelo para algo que não seja json, a string contém apenas um espaço reservado que usa chaves, e o mecanismo do modelo falha ao substituir o espaço reservado pelo valor real? Lembre-se também, como já expliquei a Duke, a pergunta original não menciona validação. Eles só queriam saber se parecia com json ou não.
Carlin.scott 6/09/16

0

Eu sugiro no modo TypeScript:

export function stringify(data: any): string {
    try {
         return JSON.stringify(data)
    } catch (e) {
         return 'NOT_STRINGIFIABLE!'
    }
}

0

Eu usei este (tipo de mistura de respostas diferentes, mas de qualquer maneira):

const isJSON = str => {
  if (typeof str === 'string'){
    try {
      JSON.parse(str)
      return true
    } catch(e){
    }
  }
  return false
}



[null, undefined, false, true, [], {}, 
 '', 'asdf', '{}', '[]', "{\"abc\": 2}","{\"abc\": \"2\"}"]
  .map(el => {
      console.log(`[>${el}<] - ${isJSON(el)}`)
})

console.log('-----------------')


0

Você pode tentar o seguinte, porque também valida número, nulo, sequência, mas a resposta acima marcada não está funcionando corretamente, é apenas uma correção da função acima:

function isJson(str) {
  try {
      const obj = JSON.parse(str);
      if (obj && typeof obj === `object`) {
        return true;
      }
    } catch (err) {
      return false;
    }
   return false;
}

-1

Além das respostas anteriores, no caso de você precisar validar um formato JSON como "{}", você pode usar o seguinte código:

const validateJSON = (str) => {
  try {
    const json = JSON.parse(str);
    if (Object.prototype.toString.call(json).slice(8,-1) !== 'Object') {
      return false;
    }
  } catch (e) {
    return false;
  }
  return true;
}

Exemplos de uso:

validateJSON('{}')
true
validateJSON('[]')
false
validateJSON('')
false
validateJSON('2134')
false
validateJSON('{ "Id": 1, "Name": "Coke" }')
true
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.