如何测试字符串是否为JSON格式?

315

我有一个简单的AJAX调用,服务器将返回一个包含有用数据的JSON字符串或由PHP函数mysql_error()生成的错误消息字符串。我该如何测试这个数据是JSON字符串还是错误消息。

希望能使用一个名为isJSON的函数,就像可以使用instanceof函数来测试某个东西是否是数组一样。

这就是我想要的:

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

可能是 AJAX: Check if a string is JSON? 的重复问题。 - Mehdi Dehghani
22个回答

478

使用 JSON.parse

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

111
异常处理不应该用于处理预期的情况。 - luisZavaleta
117
JSON.parse(1234)JSON.parse(0)JSON.parse(false)JSON.parse(null) 都不会引发异常,并且返回值为 true!!请勿使用此答案。 - Zalaboza
31
12340falsenull 都是有效的 JSON 值。如果您想要一个谓词来测试 JSON 是否表示对象,则需要进行一些额外的操作。 - Michael Lang
32
JSON.parse函数在解析字符串时会进行大量的计算,如果成功解析,就会返回JSON对象,但你正在丢弃结果,而有些用户可能想要使用它。这似乎不太好。我建议改为return {value: JSON.parse(str), valid: true};,在catch块中则改为return {value: str, valid: false};。另外,我会将函数名更改为tryParse() - Nawaz
13
@luisZavaleta,您建议采用什么方法? - PirateApp
显示剩余9条评论

141

使用JSON.parse()存在一些缺点:

  1. JSON.parse(1234)
  2. JSON.parse(0)
  3. JSON.parse(false)
  4. JSON.parse(null)

所有这些都将返回 true。

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

function testIsJson(value, expected) {
  console.log(`Expected: ${expected}, Actual: ${isJson(value)}`); 
}

// All of the following codes are expected to return false.
// But actually returns true.
testIsJson(1234, false);
testIsJson(0, false);
testIsJson(false, false);
testIsJson(null, false);

处理误报的代码

因此,我以这种方式重写了代码:

function isJson(item) {
  let value = typeof item !== "string" ? JSON.stringify(item) : item;    
  try {
    value = JSON.parse(value);
  } catch (e) {
    return false;
  }
    
  return typeof value === "object" && value !== null;
}

测试结果:

function isJson(item) {
  let value = typeof item !== "string" ? JSON.stringify(item) : item;
  try {
    value = JSON.parse(value);
  } catch (e) {
    return false;
  }

  return typeof value === "object" && value !== null;
}

function testIsJson(value, expected) {
  console.log(`Expected: ${expected}, Actual: ${isJson(value)}`);
}

const validJson = { "foo": "bar" };
const notValidJson = '{ "foo": "bar" } invalid';

// expected: true
testIsJson(validJson, true);

// expected: false
testIsJson(1234, false);
testIsJson(0, false);
testIsJson(notValidJson, false);
testIsJson(false, false);
testIsJson(null, false);


10
干得好!你的最后一个if语句可以简化为一个简单的return语句,例如:return (typeof suspect === "object" && suspect !== null); - Nebulosar
如果您使用此函数测试 .srt 文件(字幕文件),它将返回 true。 - Silvio Guedes
12340falsenull 都是有效的 JSON,并且应该返回 true。它们不是误报。 - rink.attendant.6
@rink.attendant.6 他们可能是有效的JSON,但在JSON的常见用法中,它们是无效的。我从未编写过一个期望JSON "{"random":"random"}"的程序,同时还期望任意数字。在常见用法中,我认为大多数人会认为这些是误报,或者也许我错了。我很想看到一个寻找JSON并使用这些内容的程序,即使只是为了学习一些新东西。 - undefined

97

让我们回顾一下(2019年及以后的情况)。

论点:像truefalsenull这样的值是有效的JSON (?)

事实:这些原始值是可解析为JSON,但它们并不是格式良好的JSON结构JSON 规范指出,JSON基于两种结构构建:名称/值对集合(对象)或值列表(数组)。

论点:不应该使用异常处理来执行某些预期的操作。
(这是一个有25个以上赞的评论!)

事实:不!在这种情况下,使用try/catch肯定是合法的。否则,您将需要进行大量的字符串分析,例如标记化/正则表达式操作;这会导致性能非常糟糕。

hasJsonStructure()

如果你的目标是检查某些数据/文本是否具有适当的JSON交换格式,这将非常有用。

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

用法:

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

safeJsonParse()

如果您希望在将某些数据解析为 JavaScript 值时更加谨慎,这将非常有用。

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

用法:

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

1
你链接到的 JSON 规范中说道:“JSON 文本是由符合 JSON 值语法的 Unicode 代码点序列组成的标记序列。”和“ JSON 值可以是对象、数组、数字、字符串、true、false 或 null。” - 你是如何得出 JSON 只能在根级别上是对象或数组的结论的?我没有在规范中看到这一点,也没有关于“格式良好的 JSON 结构”的任何内容。 - Relequestual
1
json.org仅提供信息。阅读您提供的规范并不支持您的建议。该规范提到了RFC8259作为最新的RFC标准。请查看只包含值的有效JSON文本示例https://tools.ietf.org/html/rfc8259#section-13 - RFC 8259旨在解决可能存在的歧义和混淆,就像此处一样。 - Relequestual
2
请再次阅读答案。我的意思是像原始类型(即RFC示例中的文本值)这样的值不是JSON“结构”。没有歧义。您可以将它们解析为JSON,这样做是有效的。但它们不是结构化数据。JSON主要是作为交换格式发明的,用于结构化数据,可以是对象或数组。 - Onur Yıldırım
2
好的,我认为我们达成了共识。根据规范,原语是有效的JSON,但不是“结构体”。这很好。但是,你说“参数:true、false、null等值是有效的JSON(?)。事实:是和不是!”——事实上,它们根据规范是有效的JSON。对于它们是否有用的观点与此事实无关。 - Relequestual
依赖于toString()并不是一个好主意,因为不同的JavaScript引擎可能会以不同的方式呈现结果。除非我错了并且toString()的值是严格规定的,否则我强烈建议不要使用这种解决方案。 - Ron S.
显示剩余3条评论

25
如果服务器响应JSON,则应具有application/json内容类型;如果它响应纯文本消息,则应具有text/plain内容类型。确保服务器以正确的内容类型响应并进行测试。

7
这是错误的,还有许多其他与 JSON 兼容的媒体类型。此外,overrideMimeType 可以覆盖内容类型标头。 - Knu
2
服务器可以传递任何类型的标头。客户端不能假设返回的内容是真正的 JSON。标头也可能被伪造。 - user2402616
我对“确保服务器响应正确的内容类型并进行测试”的理解是,您期望在正文中收到与内容类型标头的值相符的响应。但这可能不是事实。OP的问题是想要测试正文是否与内容类型匹配。您能否澄清一下您所说的“进行测试”是什么意思? - surfmuggle

18

使用jQuery $.ajax()时,如果响应为JSON,则响应将具有responseJSON属性,可以像这样进行测试:

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

3
我很怀疑这可能是大多数人真正想要的答案,甚至包括原帖作者。 - Kirby
1
这比使用try catch块更加优雅。 - Anurag Sinha

9
var parsedData;

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

然而,我建议您的http调用/服务始终以相同的格式返回数据。因此,如果出现错误,您应该有一个JSON对象来包装这个错误:

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

也许可以使用5xx代码作为HTTP状态码。


9
我只需要用两行代码就可以完成这个操作:
var isValidJSON = true;
try { JSON.parse(jsonString) } catch { isValidJSON = false }

就这些了!

但请记住有两个陷阱:
1. JSON.parse(null) 返回 null
2. 任何数字或字符串都可以使用 JSON.parse() 方法解析。
JSON.parse("5") 返回 5
JSON.parse(5) 返回 5

让我们来玩一下代码:

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

1
我已经在https://jsfiddle.net/fatmonk/gpn4eyav/上为这个答案创建了一个代码片段,其中还包括添加您自己的用户测试数据的选项。对我来说,这似乎是一个很好的库函数基础,但我想更多地了解为什么测试1不是有效的JSON数组。 - Fat Monk
因为数组必须使用 [] 来指定。例如,[1, 2, 3] 是一个数字数组。["a", "b", "c"] 是一个字符串数组。而 [{"a":1}, {"b":2}] 则是一个 JSON 数组。你的 jsfiddle 工作看起来非常有用! - efkan
就这么简单?!所以Test 1是一个JSON对象,而Test 2是由单个JSON对象元素组成的JSON数组。我理解得对吗? - Fat Monk
被标记为可能重复的问题(https://dev59.com/_nA65IYBdhLWcg3wogOe)询问如何在不使用try/catch的情况下实现此目标,因此我已经分叉了我的fiddle以尝试实现该目标。该分支位于https://jsfiddle.net/fatmonk/827jsuvr/,并且与上面的所有测试一起工作,除了Test 3,在JSON.parse处出错。有人可以建议如何避免使用try而不出错吗? - Fat Monk
你的 jsfiddle 应用程序因为 Test 3 没有有效的 JSON 表达式而抛出错误。因此,必须使用 try-catch 来捕获该错误,并在解析类似于上面的 Test 3 的表达式时将任何错误视为非 JSON 表达式进行评估:try { JSON.parse(data3) } catch(e) { isValidJSON = false } - efkan
我理解为什么会出现异常,但是我希望在运行 JSON.parse 前能够捕获异常的原因。然而现在我仔细考虑了一下,这是一个循环问题!为了避免异常,需要在运行 JSON.parse 前检查有效的 JSON,但是检查有效的 JSON 正是我们试图使用该函数执行的任务。所以我尝试创建的函数需要调用另一个函数,该函数可以执行与我的函数旨在实现的相同任务 - 使得我的函数没有意义。所以我有点傻! - Fat Monk

7

我喜欢最佳答案,但如果它是一个空字符串,它会返回true。因此,这里有一个修复方法:

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

1
变量json没有被使用?还是只是为了捕获错误? - stackdave

5
也许有一些测试可以做,例如如果您知道返回的JSON始终会被{}包围,那么您可以测试这些字符或其他一些hacky方法。或者您可以使用json.org JS库来尝试解析它并测试是否成功。
然而,我建议采用不同的方法。您的PHP脚本当前在调用成功时返回JSON,但在失败时返回其他内容。为什么不总是返回JSON呢?
例如:
成功调用:
{ "status": "success", "data": [ <your data here> ] }

错误调用:

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

这将使编写客户端JS变得更加容易——您只需检查“status”成员,然后相应地采取行动。

5

好的...这取决于您接收数据的方式。我认为服务器正在响应一个JSON格式的字符串(例如在PHP中使用json_encode())。如果您使用JQuery post并将响应数据设置为JSON格式,但它是一个格式错误的JSON,那么这将会产生一个错误:

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

但是,如果你使用类型响应作为文本,你需要使用$.parseJSON。根据 jQuery 网站的说明: "传入格式错误的 JSON 字符串可能会导致抛出异常"。因此你的代码应该是:

$.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',
});

除非你正在尝试解析上面示例中错误函数中的错误文本,并且不确定它是否为JSON,否则请返回已翻译的文本。 - Kirby
很好的答案,但是如果“response”为空,它将进入“success”:'( - Henrik Petterson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接