I have a simple AJAX call, and the server will return either a JSON string with useful data or an error message string produced by the PHP function mysql_error()
. How can I test whether this data is a JSON string or the error message.
It would be nice to use a function called isJSON
just like you can use the function instanceof
to test if something is an Array.
This is what I want:
if (isJSON(data)){
//do some data stuff
}else{
//report the error
alert(data);
}
This question is related to
javascript
mysql
json
Use JSON.parse
function isJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
This code is JSON.parse(1234)
or JSON.parse(0)
or JSON.parse(false)
or JSON.parse(null)
all will return true.
function isJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
So I rewrote code in this way:
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;
}
Testing result:
Let's recap this (for 2019+).
Argument: Values such as
true
,false
,null
are valid JSON (?)
FACT: These primitive values are JSON-parsable but they are not well-formed JSON structures. JSON specification indicates JSON is built on on two structures: A collection of name/value pair (object) or an ordered list of values (array).
Argument: Exception handling shouldn't be used to do something expected.
(This is a comment that has 25+ upvotes!)
FACT: No! It's definitely legal to use try/catch, especially in a case like this. Otherwise, you'd need to do lots of string analysis stuff such as tokenizing / regex operations; which would have terrible performance.
hasJsonStructure()
This is useful if your goal is to check if some data/text has proper JSON interchange format.
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()
And this is useful if you want to be careful when parsing some data to a JavaScript value.
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);
}
If the server is responding with JSON then it would have an application/json
content-type, if it is responding with a plain text message then it should have a text/plain
content-type. Make sure the server is responding with the correct content-type and test that.
when using jQuery $.ajax()
the response will have the responseJSON
property if the response was JSON, this could be checked like this:
if (xhr.hasOwnProperty('responseJSON')) {}
I like best answer but if it is an empty string it returns true. So here's a fix:
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;
}
I use just 2 lines to perform that:
var isValidJSON = true;
try { JSON.parse(jsonString) } catch { isValidJSON = false }
That's all!
But keep in mind there are 2 traps:
1. JSON.parse(null)
returns null
2. Any number or string can be parsed with JSON.parse()
method.
JSON.parse("5")
returns 5
JSON.parse(5)
returns 5
Let's some play on 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
var parsedData;
try {
parsedData = JSON.parse(data)
} catch (e) {
// is not a valid JSON string
}
However, I will suggest to you that your http call / service should return always a data in the same format. So if you have an error, than you should have a JSON object that wrap this error:
{"error" : { "code" : 123, "message" : "Foo not supported" } }
And maybe use as well as HTTP status a 5xx code.
Well... It depends the way you are receiving your data. I think the server is responding with a JSON formated string (using json_encode() in PHP,e.g.). If you're using JQuery post and set response data to be a JSON format and it is a malformed JSON, this will produce an error:
$.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!"); }
});
But, if you're using the type response as text, you need use $.parseJSON. According jquery site: "Passing in a malformed JSON string may result in an exception being thrown". Thus your code will be:
$.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',
});
There are probably tests you can do, for instance if you know that the JSON returned is always going to be surrounded by {
and }
then you could test for those characters, or some other hacky method. Or you could use the json.org JS library to try and parse it and test if it succeeds.
I would however suggest a different approach. Your PHP script currently returns JSON if the call is successful, but something else if it is not. Why not always return JSON?
E.g.
Successful call:
{ "status": "success", "data": [ <your data here> ] }
Erroneous call:
{ "status": "error", "error": "Database not found" }
This would make writing your client side JS much easier - all you have to do is check the "status" member and the act accordingly.
Here is a code with some minor modification in Bourne's answer. As JSON.parse(number) works fine without any exception so added isNaN.
function isJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return isNaN(str);
}
Warning: For methods relying on JSON.parse
- Arrays and quote surrounded strings will pass too (ie. console.log(JSON.parse('[3]'), JSON.parse('"\uD800"'))
)
To avoid all non-object JSON primitives (boolean, null, array, number, string), I suggest using the following:
/* 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
}
}
Code Explanation
Why not use the hasJsonStructure() answer?
Relying on toString()
is not a good idea. This is because different JavaScript Engines may return a different string representation. In general, methods which rely on this may fail in different environments or may be subject to fail later should the engine ever change the string result
Why is catching an exception not a hack?
It was brought up that catching an exception to determine something's validity is never the right way to go. This is generally good advice, but not always. In this case, exception catching is likely is the best route because it relies on the JavaScript engine's implementation of validating JSON data.
Relying on the JS engine offers the following advantages:
When given the opportunity to lean on the JavaScript engine, I'd suggest doing it. Particularly so in this case. Although it may feel hacky to catch an exception, you're really just handling two possible return states from an external method.
I used this one (kind of mix of different answers, but anyway):
const isJSON = str => {_x000D_
if (typeof str === 'string'){_x000D_
try {_x000D_
JSON.parse(str)_x000D_
return true_x000D_
} catch(e){_x000D_
}_x000D_
}_x000D_
return false_x000D_
}_x000D_
_x000D_
_x000D_
_x000D_
[null, undefined, false, true, [], {}, _x000D_
'', 'asdf', '{}', '[]', "{\"abc\": 2}","{\"abc\": \"2\"}"]_x000D_
.map(el => {_x000D_
console.log(`[>${el}<] - ${isJSON(el)}`)_x000D_
})_x000D_
_x000D_
console.log('-----------------')
_x000D_
All json strings start with '{' or '[' and end with the corresponding '}' or ']', so just check for that.
Here's how Angular.js does it:
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
You could try decoding it and catching the exception (native or json2.js):
try {
newObj = JSON.parse(myJsonString);
} catch (e) {
console.log('Not JSON');
}
However, I would suggest making the response always be valid JSON. If you get an error back from your MySQL query, simply send back JSON with the error:
{"error":"The MySQL error string."}
And then:
if (myParsedJSON.error) {
console.log('An error occurred: ' + myParsedJSON.error);
}
You can try the following one because it also validates number, null, string but the above-marked answer is not working correctly it's just a fix of the above function:
function isJson(str) {
try {
const obj = JSON.parse(str);
if (obj && typeof obj === `object`) {
return true;
}
} catch (err) {
return false;
}
return false;
}
In addition to previous answers, in case of you need to validate a JSON format like "{}", you can use the following code:
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;
}
Examples of usage:
validateJSON('{}')
true
validateJSON('[]')
false
validateJSON('')
false
validateJSON('2134')
false
validateJSON('{ "Id": 1, "Name": "Coke" }')
true
Source: Stackoverflow.com