JSON.parse返回字符串而不是对象

56

我正在编写一个 WebSocket 客户端,希望能够接收 JSON 格式的消息。为此,我需要进行登录。如果登录不成功,我将发送一个包含“nosuccess”的 JSON 字符串。

JSON 字符串:
{"action":"login","args":["nosuccess"]}

在客户端,我使用以下代码获取字符串:
WebSocket socket = new WebSocket("ws://localhost:2555/api");

socket.onmessage = function(evt) {
    console.log(evt.data);
    console.log(typeof(evt.data));
    onMessage(evt);
}
function onMessage(evt) {
var data = JSON.parse(evt.data);
var action = data.action;
var args = data.args;
console.log(data);
console.log(typeof(data));
console.log(action);
console.log(args);

但数据类型是字符串...为什么呢?

evt.data返回:

 "{\"action\":\"login\",\"args\":[\"nosuccess\"]}"

数据返回:

 {"action":"login","args":["nosuccess"]}

WebSocket服务器是一个Jetty Server,它使用Google的Gson将字符串和字符串数组以json格式发送,并进行解析。Class是一个包含String action和String array args的类。

websocket.js的完整源代码:

var socket;

function openWebsocket(adress) {
    socket = new WebSocket(adress);
    socket.onopen = function(evt) {
        console.log("Socket opened [" + adress + "]");
    };
    socket.onclose = function(evt) {
        loadPage("login.html");
        console.log("Socket closed [" + evt.code + "]");
    }
    socket.onmessage = function(evt) {
        onMessage(evt);
    }
    socket.onerror = function(evt) {
        console.log("Socket couldn't connect [" + evt.message + "]");
        showMessage("fa-exclamation-circle", "Socket couldn't be established!", 1000);
    }
}

function onMessage(evt) {
    var data = JSON.parse(evt.data);
    var action = data.action;
    var args = data.args;
    console.log(data);
    console.log(typeof(data));
    console.log(action);
    console.log(args);
    $(".card-container h3").html(data);

    if(action == "login") {
        if(args[0] == "success") {
            loadPage("dashboard.htm");
            currentpage = "dashboard.htm";
            showMessage("fa-check", "Du wurdest erfolgreich eingeloggt", 2000);
        } else if(args[0] == "nosuccess") {
            loadPage("login.html");
            currentpage = "login.html";
            showMessage("fa-exclamation-circle", "Falscher Benutzername oder falsches Passwort", 2000);
        } else if(args[0] == "unauthenticated") {
            loadPage("login.html");
            currentpage = "login.html";
            showMessage("fa-exclamation-circle", "Login failure: not authenticated", 2000);
        }
    }

}

function sendMessage(json) {
    $(".card-container h3").html(JSON.stringify(json));
    console.log(JSON.stringify(json));
    socket.send(JSON.stringify(json));
}

如果我更改这一行:
 var data = JSON.parse(evt.data);

转换为:

var data = JSON.parse("{\"action\":\"login\",\"args\":[\"nosuccess\"]}");

然后它是一个json对象,但当我使用evt.data时,它是一个字符串。如果我将该行更改为:

    var data = JSON.parse(JSON.parse(evt.data));

然后它就可以运行了,但是为什么呢?通常情况下应该只需要一次JSON.parse,对吧?

http://jsbin.com/morutob/1/edit?js,console — 我无法重现这个问题。你可能对evt.data的值有误解。 - Quentin
如果您手动编写并获取一个对象,但从evt.data中获取它而不是手动编写,则您手动编写的内容和从evt.data中获取的内容是不同的,因此您可能对evt.data的值存在误解。 - Quentin
但是对于我手动使用的方法,我使用了从evt.data获取的字符串。为什么这不一样呢? - Nightloewe
你正在以某种方式对它进行更改。根据你在问题中所说的内容,我无法看出具体情况。 - Quentin
2
我曾经遇到过完全相同的问题,原因是一个对象被字符串化了两次。 - andy mccullough
显示剩余3条评论
4个回答

69

这似乎与过度字符串化的字符串相当一致。例如,我使用FileReader.readAsText加载了一个包含\n\r的文本文件,这在控制台中呈现出来,所以我首先执行了(JSON.stringify(reader.result)).replace(/(?:\\[rn])+/g, '')以查看这些符号,然后消除它们。将此操作结果用JSON.parse()转换为非转义字符串,再次运行JSON.parse()即可创建对象。

如果您不将字符串字符串化,则会将其转换为对象,并且通常情况下这并不是必需的,但如果您无法控制获得的值,则运行两次JSON.parse()即可解决问题。


根据JSON.parse()的说明,JSON.parse()返回的是与给定JSON文本对应的对象、数组、字符串、数字、布尔值或null值。所以,首先,我需要记住JSON可以用来表示不仅仅是对象(我认为令人困惑的是你可以将字符串转化为JSON字符串化),其次,我们需要进行与JSON字符串化次数相同的次数的“非字符串化”(解析)。 - undefined

13
@DerpSuperleggera正确。问题确实是一个过度字符串化的字符串。
let arr = "[\"~#iM\",[\"is_logged_out_token\",false,\"token_type\",\"Bearer\"]]"

因此,要将其解析为对象,我只需通过 JSON.parse 两次运行它就可以了,这样做就行了!

let obj = JSON.parse(JSON.parse(arr))

5

这个回复是正确的,看起来是由于字符串过度转义引起的问题。我在我的项目中使用AWS Amplify AWSJSON类型时遇到了这个问题。对我有效的解决方案是迭代两次以获取对象。

编写了一个简单的JS函数,当用于解析时将返回一个对象。它没有真正的错误检查,但是可以作为一个起点。

    export function jsonParser(blob) {
       let parsed = JSON.parse(blob);
       if (typeof parsed === 'string') parsed = jsonParser(parsed);
       return parsed;
    }


3

正如其他评论中所述,这个问题似乎已经解决了。如果您从服务器接收到一个“字符串化的对象”的响应,则可以使用JSON.parse()将其转换为普通对象,方法如下:

var stringResponse = '{"action":"login","args":["nosuccess"]}';

var objResponse = JSON.parse(stringResponse);

console.log(objResponse.args);

您可以在这里尝试上述代码。
至于为什么服务器返回字符串而不是对象,这实际上取决于您的后端代码、使用的库和传输协议。如果您只想让前端代码工作,请使用JSON.parse。如果您想编辑后端响应的方式,请提供更多信息。

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