Comment tester si une chaîne est JSON ou non?

191

J'ai un simple appel AJAX et le serveur renverra soit une chaîne JSON avec des données utiles, soit une chaîne de message d'erreur produite par la fonction PHP mysql_error(). Comment puis-je tester si ces données sont une chaîne JSON ou le message d'erreur.

Ce serait bien d'utiliser une fonction appelée isJSONcomme vous pouvez utiliser la fonction instanceofpour tester si quelque chose est un tableau.

C'est ce que je veux:

if (isJSON(data)){
    //do some data stuff
}else{
    //report the error
    alert(data);
}
jeffery_the_wind
la source
Peut-être en utilisant eval()s'il retourne undefinedalors, ce n'est pas JSON
MatuDuke
4
Cela a été résolu ici: stackoverflow.com/questions/3710204/…
Reinard
2
Merci à tous, désolé de ne pas avoir trouvé cet autre article auparavant.
jeffery_the_wind
1
Techniquement ce n'est pas une dupe de 3710204 puisque l'on demande si c'est json valide qui est une barre beaucoup plus haute à passer que ce n'est pas du tout json.
carlin.scott
Double possible d' AJAX: vérifier si une chaîne est JSON?
Mehdi Dehghani

Réponses:

324

Utilisez JSON.parse

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}
Bourne
la source
53
La gestion des exceptions ne doit pas être utilisée pour faire quelque chose d'attendu.
luisZavaleta
46
JSON.parse(1234)OU JSON.parse(0)OU JSON.parse(false)OU JSON.parse(null)tout ne lèvera pas d'exception et retournera vrai !!. ne pas utiliser cette réponse
Zalaboza
19
@Zalaboza 1234, 0, falseet nullsont toutes les valeurs de JSON valides. Si vous voulez un prédicat qui teste si le JSON représente un objet, vous devrez en faire un peu plus.
Michael Lang
20
JSON.parsefait beaucoup de calculs pour analyser la chaîne et vous donne l'objet json s'il réussit, mais vous rejetez le résultat que certains utilisateurs pourraient vouloir utiliser. Cela ne semble pas bon. Je voudrais plutôt return {value: JSON.parse(str), valid: true};et dans le bloc catch return {value: str, valid: false};.. et je changerais le nom de la fonction en tryParse().
Nawaz
7
@luisZavaleta alors que suggérez-vous comme méthode
PirateApp
80

Ce code est JSON.parse(1234)ou JSON.parse(0)ou JSON.parse(false)ou JSON.parse(null)tout retournera vrai.

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

J'ai donc réécrit le code de cette manière:

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

Résultat du test:

résultat du test isJson

kubosho_
la source
4
Bon travail! Votre dernière instruction if pourrait être simplifiée en une simple déclaration de retour telle que:return (typeof suspect === "object" && suspect !== null);
Nebulosar
39

Récapitulons ceci (pour 2019+).

Argument : Les valeurs telles que true, false, nullsont valables JSON (?)

FAIT : Ces valeurs primitives sont analysables par JSON mais ce ne sont pas des structures JSON bien formées . La spécification JSON indique que JSON repose sur deux structures: une collection de paires nom / valeur (objet) ou une liste ordonnée de valeurs (tableau).

Argument : la gestion des exceptions ne doit pas être utilisée pour faire quelque chose d'attendu.
(Ceci est un commentaire qui a plus de 25 votes positifs!)

FAIT : Non! Il est tout à fait légal d'utiliser try / catch, surtout dans un cas comme celui-ci. Sinon, vous devrez effectuer de nombreuses analyses de chaînes telles que des opérations de tokenisation / regex; ce qui aurait des performances terribles.

hasJsonStructure()

Ceci est utile si votre objectif est de vérifier si certaines données / textes ont le format d'échange JSON approprié.

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

Usage:

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

safeJsonParse()

Et cela est utile si vous voulez être prudent lors de l'analyse de certaines données en une valeur JavaScript.

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

Usage:

const [err, result] = safeJsonParse('[Invalid JSON}');
if (err) {
    console.log('Failed to parse JSON: ' + err.message);
} else {
    console.log(result);
}
Onur Yıldırım
la source
1
Vous liez à la spécification JSON dit ce qui suit: "Un texte JSON est une séquence de jetons formée à partir de points de code Unicode qui est conforme à la grammaire de la valeur JSON." et "Une valeur JSON peut être un objet, un tableau, un nombre, une chaîne, vrai, faux ou nul." - Comment êtes-vous arrivé à la conclusion qu'un JSON ne peut être qu'un objet ou un tableau au niveau racine? Je ne peux pas voir cela dans les spécifications, ni rien concernant les "structures JSON bien formées"
Relequestual
Lisez le deuxième paragraphe qui commence par "JSON est construit sur deux structures ..." @ json.org ou 4e et 5e paragraphes de ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
Onur Yıldırım
json.org est uniquement informatif. La lecture de la spécification que vous avez liée ne prend pas en charge votre suggestion. La spécification mentionne la RFC 8259 comme la dernière RFC. Jetez un œil à des exemples de texs JSON valides contenant uniquement des valeurs tools.ietf.org/html/rfc8259#section-13 - La RFC 8259 est conçue pour résoudre les ambiguïtés et confusions possibles, tout comme ceci.
Relequestual
Relisez la réponse. Je dis que les valeurs telles que les primitives (c'est-à-dire les valeurs de texte dans les exemples RFC) ne sont pas des «structures» JSON. Il n'y a pas d'ambiguïtés. Vous POUVEZ les analyser comme JSON, il est valide de le faire. Mais ce ne sont pas des données structurées. JSON est principalement inventé comme un format d'échange «qui est utilisé pour les données structurées» qui peuvent être un objet ou un tableau.
Onur Yıldırım
1
OK, donc je pense que nous sommes d'accord. Les primatives sont des JSON valides selon la spécification, mais ne sont pas des «structures». C'est très bien. Mais, vous avez dit "Argument: les valeurs telles que true, false, null sont des JSON valides (?). Fait: Oui et non!" - Le fait est que le JSON est valide selon la spécification. Les opinions sur leur utilité ou non ne sont pas pertinentes pour ce fait.
Relequestual
20

Si le serveur répond avec JSON, il aura un application/jsontype de contenu, s'il répond avec un message en texte brut, il doit avoir un text/plaintype de contenu. Assurez-vous que le serveur répond avec le type de contenu correct et testez-le.

Quentin
la source
4
C'est faux, il existe de nombreux autres types de médiateurs compatibles json. En outre, overrideMimeTypepeut remplacer l'en-tête de type de contenu.
Knu
14

lors de l'utilisation de jQuery $.ajax()la réponse aura la responseJSONpropriété si la réponse était JSON, cela pourrait être vérifié comme ceci:

if (xhr.hasOwnProperty('responseJSON')) {}
rémy
la source
3
Je suppose que c'est vraiment la réponse que la plupart des gens recherchent, probablement même l'OP
Kirby
1
C'est beaucoup plus élégant que d'utiliser le bloc try catch
Anurag Sinha
6

J'aime la meilleure réponse, mais s'il s'agit d'une chaîne vide, elle renvoie true. Alors, voici une solution:

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;
}
Lonnie Price Sr.
la source
var json n'est pas utilisé? ou juste pour attraper l'erreur?
stackdave
5
var parsedData;

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

Cependant, je vais vous suggérer que votre appel / service http doit toujours renvoyer une donnée dans le même format. Donc, si vous avez une erreur, vous devriez avoir un objet JSON qui encapsule cette erreur:

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

Et peut-être utiliser ainsi que l'état HTTP un code 5xx.

ZER0
la source
5

Eh bien ... Cela dépend de la façon dont vous recevez vos données. Je pense que le serveur répond avec une chaîne au format JSON (en utilisant json_encode () en PHP, par exemple). Si vous utilisez JQuery post et définissez les données de réponse au format JSON et qu'il s'agit d'un JSON malformé, cela produira une erreur:

$.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!"); }
});

Mais, si vous utilisez la réponse de type sous forme de texte, vous devez utiliser $ .parseJSON. Selon le site jquery: "La transmission d'une chaîne JSON mal formée peut entraîner la levée d'une exception". Ainsi votre code sera:

$.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',
});
Lucas Batistussi
la source
sauf si, bien sûr, vous essayez d'analyser le texte d'erreur dans la fonction d'erreur dans l'exemple ci-dessus et que vous ne savez pas s'il s'agit de JSON ...
Kirby
Excellente réponse, même si elle responseest vide, elle ira à success: '(
Henrik Petterson
4

Il y a probablement des tests que vous pouvez faire, par exemple si vous savez que le JSON retourné sera toujours entouré par {et que }vous pourriez alors tester ces caractères, ou une autre méthode hacky. Ou vous pouvez utiliser le json.org bibliothèque JS pour essayer de l'analyser et tester si elle réussit.

Je suggérerais cependant une approche différente. Votre script PHP renvoie actuellement JSON si l'appel réussit, mais autre chose si ce n'est pas le cas. Pourquoi ne pas toujours renvoyer JSON?

Par exemple

Appel réussi:

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

Appel erroné:

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

Cela rendrait l'écriture de votre JS côté client beaucoup plus facile - tout ce que vous avez à faire est de vérifier le membre "status" et d'agir en conséquence.

mdm
la source
4

J'utilise juste 2 lignes pour effectuer cela:

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

C'est tout!

Mais gardez à l'esprit qu'il y a 2 pièges:
1. JSON.parse(null)renvoie null
2. N'importe quel nombre ou chaîne peut être analysé avec la JSON.parse()méthode.
   JSON.parse("5")retourne 5
   JSON.parse(5)retours5

Jouons un peu sur le code:

// 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
efkan
la source
J'ai créé un violon pour cette réponse sur jsfiddle.net/fatmonk/gpn4eyav qui inclut également la possibilité d'ajouter vos propres données de test utilisateur. Cela me semble être la base d'une bonne fonction de bibliothèque, mais j'aimerais en savoir plus sur les raisons pour lesquelles le test 1 n'est pas un tableau JSON valide.
Fat Monk
Parce qu'un tableau doit être spécifié à l'aide de [et ]. Par exemple, [1, 2, 3]est un tableau de nombres. ["a", "b", "c"]est un tableau de chaînes. Et [{"a":1}, {"b":2}]est un tableau JSON. Votre travail jsfiddle semble vraiment utile!
efkan le
Aussi simple que cela?! Donc Test 1 est un objet JSON et Test 2 est un tableau JSON constitué d'un seul élément d'objet JSON. Ai-je bien compris?
Fat Monk
La question signalée comme une duplication possible de ceci ( stackoverflow.com/questions/3710204/… ) demande de parvenir à cela sans utiliser try / catch, donc j'ai bifurqué mon violon pour essayer d'atteindre cet objectif également. Le fork se trouve sur jsfiddle.net/fatmonk/827jsuvr et fonctionne avec tous les tests ci-dessus, à l'exception du test 3 qui comporte des erreurs au niveau du fichierJSON.parse . Quelqu'un peut-il conseiller comment éviter cette erreur sans utiliser try?
Fat Monk
Votre jsfiddleapplication génère une erreur car le test 3 n'a pas d'expression JSON valide. Donc, un try-catchdoit être utilisé pour attraper cette erreur et pour évaluer toute erreur car l'expression n'est pas JSON lors de l'analyse comme le test 3 ci-dessus:try { JSON.parse(data3) } catch(e) { isValidJSON = false }
efkan
2

Vous pouvez essayer de le décoder et d'attraper l' exception (native ou json2.js ):

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

Cependant, je suggérerais que la réponse soit toujours JSON valide. Si vous obtenez une erreur de votre requête MySQL, renvoyez simplement JSON avec l'erreur:

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

Puis:

if (myParsedJSON.error) {
  console.log('An error occurred: ' + myParsedJSON.error);
}
James Sumners
la source
2

Attention: pour les méthodes reposant sur JSON.parse- Les tableaux et les guillemets entourés de chaînes passeront également (ie.console.log(JSON.parse('[3]'), JSON.parse('"\uD800"')) )

Pour éviter toutes les primitives JSON non-objet (booléenne, null, tableau, nombre, chaîne), je suggère d'utiliser ce qui suit:

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

Explication du code

  • !! o - Non faux (exclut null, qui s'enregistre comme typeof 'object')
  • (typeof o === 'objet') - Exclut le booléen, le nombre et la chaîne
  • ! Array.isArray (o) - Exclut les tableaux (qui s'inscrivent comme typeof 'object')
  • try ... JSON.stringify / JSON.parse - Demande au moteur JavaScript de déterminer si JSON valide

Pourquoi ne pas utiliser la réponse hasJsonStructure ()?

Reposant sur toString() n'est pas une bonne idée. En effet, différents moteurs JavaScript peuvent renvoyer une représentation sous forme de chaîne différente. En général, les méthodes qui reposent sur cela peuvent échouer dans différents environnements ou peuvent être sujettes à l'échec plus tard si le moteur change le résultat de la chaîne

Pourquoi attraper une exception n'est-il pas un hack?

On a évoqué le fait qu'attraper une exception pour déterminer la validité de quelque chose n'est jamais la bonne voie à suivre. C'est généralement un bon conseil, mais pas toujours. Dans ce cas, la capture d'exceptions est probablement la meilleure voie, car elle repose sur l'implémentation du moteur JavaScript pour valider les données JSON.

S'appuyer sur le moteur JS offre les avantages suivants:

  1. Plus complet et continuellement à jour à mesure que les spécifications JSON changent
  2. Probablement plus rapide (car il s'agit d'un code de niveau inférieur)

Lorsqu'on me donne la possibilité de s'appuyer sur le moteur JavaScript, je suggère de le faire. Surtout dans ce cas. Bien qu'il puisse se sentir hacky pour attraper une exception, vous êtes vraiment manipuler juste deux états de retour possible d'une méthode externe.

Ron S.
la source
1

Voici un code avec quelques modifications mineures dans la réponse de Bourne. Comme JSON.parse (nombre) fonctionne bien sans aucune exception, il a donc été ajouté isNaN.

function isJson(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return isNaN(str);
}
Mamgain dévash
la source
0

Toutes les chaînes json commencent par '{' ou '[' et se terminent par le '}' ou ']' correspondant, alors vérifiez simplement cela.

Voici comment Angular.js le fait:

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

carlin.scott
la source
@DukeDougal voulez-vous clarifier? Parfois, les gens commencent leur json avec un '[' mais ce n'est pas très courant.
carlin.scott
1
Vous devez l'analyser pour que le JSON soit valide. S'il s'agit d'un JSON invalide, ce n'est pas du JSON. La question est "comment savoir si une chaîne est JSON ou non?". Selon votre approche, ce serait JSON {fibble - et ce n'est vraiment pas JSON. Considérez également des cas comme le numéro 1 seul - c'est JSON valide.
Duke Dougal

1
"S'il s'agit d'un JSON invalide, ce n'est pas du JSON". Le fait que vous deviez utiliser le mot «valide» montre que vous ajoutez une qualification au fait que c'est plus que juste json. La question était simplement "is it json" et mon exemple de code répond parfaitement à cette question sans supposer d'exigences supplémentaires.
carlin.scott

mauvaise idée si vous utilisez certains systèmes de modèles et que vous avez quelque chose comme { someValue }passera automatiquement la validation.
ncubica le

@ncubica donc vous utilisez un modèle pour autre chose que json, la chaîne ne contient qu'un espace réservé qui utilise des accolades, et le moteur de modèle ne parvient pas à remplacer l'espace réservé par la valeur réelle? Gardez également à l'esprit, comme je l'ai déjà expliqué à Duke, la question originale ne mentionne pas la validation. Ils voulaient seulement savoir si cela ressemblait à json ou non.
carlin.scott

0

Je suggère en mode Typescript:

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

0

J'ai utilisé celui-ci (sorte de mélange de réponses différentes, mais de toute façon):

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

Vous pouvez essayer la suivante car elle valide également nombre, null, chaîne, mais la réponse indiquée ci-dessus ne fonctionne pas correctement, c'est juste une correction de la fonction ci-dessus:

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

-1

En plus des réponses précédentes, au cas où vous auriez besoin de valider un format JSON tel que "{}", vous pouvez utiliser le code suivant:

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

Exemples d'utilisation:

validateJSON('{}')
true
validateJSON('[]')
false
validateJSON('')
false
validateJSON('2134')
false
validateJSON('{ "Id": 1, "Name": "Coke" }')
true
K.King