JSON.parse returns string instead of object - javascript

im writing a websocket client and i would like to receive messages as json strings. For this I need a login. And if the login isn't true i send a json string with nosuccess.
JSON String:
{"action":"login","args":["nosuccess"]}
On the client I'm using this to get the string:
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);
But the type of data is a string...
But why?
evt.data returns:
"{\"action\":\"login\",\"args\":[\"nosuccess\"]}"
data returns:
{"action":"login","args":["nosuccess"]}
The WebSocket server is a jetty Server which sends a string and a string array in json parsed in json with gson.toJson(class) Gson by Google. The Class is a class containing String action and String array args.
Complete source code of 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));
}
If I change this line:
var data = JSON.parse(evt.data);
to this:
var data = JSON.parse("{\"action\":\"login\",\"args\":[\"nosuccess\"]}");
Then it is a json object, but when I use evt.data then it is a string.
If I change the line to this:
var data = JSON.parse(JSON.parse(evt.data));
Then it works, but why, normally it should do it with only one JSON.parse, should it?

This seems to be fairly consistent with over-stringified strings. For example I loaded a text file using FileReader.readAsText that came with \n and \r which rendered in the console, so I did - (JSON.stringify(reader.result)).replace(/(?:\\[rn])+/g, '') first to see the symbols, then to get rid of them. Taking that and running JSON.parse() on it converts it to a non-escaped string, so running JSON.parse() again creates an object.
If you do not stringify your string, it will convert to an object and often it is not necessary but if you have no control over the obtained value, then running JSON.parse() twice will do the trick.

#DerpSuperleggera is correct. The issue was indeed an over-stringified string
let arr = "[\"~#iM\",[\"is_logged_out_token\",false,\"token_type\",\"Bearer\"]]"
So to parse it as an object, I just ran it through JSON.parse twice and that did the trick!
let obj = JSON.parse(JSON.parse(arr))

As others have stated in the comments it seems that this issue has been solved. If you are receiving a response from the server as a "stringified object" then you can turn it into a normal object with JSON.parse() like so:
var stringResponse = '{"action":"login","args":["nosuccess"]}';
var objResponse = JSON.parse(stringResponse);
console.log(objResponse.args);
You can also try out the above code here.
As for why the server is returning a string when you really wanted an object, that really comes down to your backend code, what library you are using, and the transport protocol. If you just want your front-end code to work, use JSON.parse. If you want to edit how the backend responds, please provide more information.

The checked response is correct in that it seems to be an issue of over-stringified strings. I came across it from using AWS Amplify AWSJSON type in my project. The solution that worked for me was to iterate multiple (twice) to get an object.
Wrote a simple JS function that when used to parse will return an object. There isn't really error checking, but a starting point.
export function jsonParser(blob) {
let parsed = JSON.parse(blob);
if (typeof parsed === 'string') parsed = jsonParser(parsed);
return parsed;
}

Related

If i pass serialized json data, where does that appear in the http request?

I am attempting to do this: Insert a http validation token into serialized JSON data. I already have working the ability to pass it normally in JSON and also to pass it in a header using a custom authorization extension. What I would like to do is, in the validator, if the request isn't in the header data or in the form data, deserialize the passed data and look in there. I can't actually find the data in the request though. When I read the passed data (84 bytes), i just ended up with a bunch of numbers.
Here's the validator for antiforgery:
namespace DispatchCrude.Extensions
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public sealed class ValidateHeaderAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var httpContext = filterContext.HttpContext;
var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
//try header first
var token = httpContext.Request.Headers["__RequestVerificationToken"];
//try normal method
if (token == null)
{
for(int cl = 0; cl < httpContext.Request.Form.Keys.Count; cl++)
{
if (httpContext.Request.Form.Keys[cl] == "__RequestVerificationToken")
{
token = httpContext.Request.Form[httpContext.Request.Form.Keys[cl]];
}
}
}
if (token == null)
{
//this section isn't finding the passed data
//get here if form stringified
System.IO.Stream str; String strmContents;
Int32 counter, strLen, strRead;
// Create a Stream object.
str = httpContext.Request.InputStream;
// Find number of bytes in stream.
strLen = Convert.ToInt32(str.Length);
// Create a byte array.
byte[] strArr = new byte[strLen];
// Read stream into byte array.
strRead = str.Read(strArr, 0, strLen);
// Convert byte array to a text string.
strmContents = "";
for (counter = 0; counter < strLen; counter++)
{
strmContents = strmContents + strArr[counter].ToString();
}
token = strmContents; //just so i can debug the returned token
}
//if both fail, attempt to check stringified data for matching token.
AntiForgery.Validate(cookie != null ? cookie.Value : null, token);
}
}
}
and here's some example code we are serializing the form data:
$.ajax({
type: "POST",
headers: appendVerificationToken(),
url: "/OrderSettlement/SessionApplyRatesCancel",
data: JSON.stringify({ key: data.key }),
contentType: "application/json",
success: function () {
/* decided to NOT close the window (just cancel and show the [CANCELLED] description)
// on success, close the owning popup
runFuncByNameSingleParameter(_owningPopup.Close);
*/
}
});
As I said, passing it in the header works, but I would like to be able to add it to the data instead and I can't find it in the request to parse it.
thanks to Ouroborus, I was able to come up with a solution.
This works:
string json;
var position = httpContext.Request.InputStream.Position;
using (var reader = new StreamReader(httpContext.Request.InputStream, Encoding.UTF8, false, 8192, true))
{
json = reader.ReadToEnd();
//you MUST reset the input stream to start or you will break post.
httpContext.Request.InputStream.Seek(position, SeekOrigin.Begin);
try
{
var jsonObj = Json.Decode(json); //attempt to parse into json object.
token = jsonObj["__RequestVerificationToken"];
}
catch
{
//eat the parse errors. That simply means it's not json.
}
}
I hope it helps someone else. Note that you must reset the stream back to zero, exactly how I'm doing it, or the controller function won't receive any passed data.

MobileFirst POST javascript array to java array

I have an array in javascript that I am trying to pass to my mobilefirst java adapter. I call my adapter like so,
myArr = [1,2,3];
var sendPost = new WLResourceRequest(
"/adapters/MyAdpater/path",
WLResourceRequest.POST
);
var formParam = {"arr":myArr};
sendTicketPost.sendFormParameters(formParams);
Then in my adapter I can have my method and get the param
public JSONObject postAdapterFx(#FormParam("arr") List<Integer> myArray) {}
Currently when I send this I just get a 400 error and it is because the adapter doesnt like the form param as that type, so what else could I set myArray to in the adapter? I can send it as a string and then convert the string to a List<Integer> in the java but that is really messy and I would like to avoid doing that.
So how can I pass this array?
Thanks for the help
you can do it in body with request.send(yourArray). Then you can read it in Java with buffer.
Example from knowledge center
var request = WLResourceRequest(url, method, timeout);
request.send(json).then(
function(response) {
// success flow
},
function(error) {
// fail flow
}
);
You will need to take an extra step before sending the form data to the adapter. sendFormParameters only accepts objects with simple values i.e., string, integer, and boolean; in your case arr is an array.
Create a utility function that will encode the form data, you can use the following:
function encodeFormData(data) {
var encoded = '';
for(var key in data) {
var value = data[key];
if(value instanceof Array) {
encoded += encodeURI(key+'='+value.join('&'+key+'='));
} else {
encoded += encodeURI(key+'='+value);
}
encoded += '&';
}
return encoded.slice(0, -1);
}
Then you will need to update your code as follows:
var myArr = [9,2,3];
var sendPost = new WLResourceRequest("/adapters/Cool/users/cool", WLResourceRequest.POST);
var formParam = {"arr" : myArr};
sendPost.sendFormParameters(encodeFormData(formParam));

javascript object, how to assign base64 type

I am using SecuGen device and its library.
I wrote following code
function fnCapture() {
document.frmmain.objFP.Capture();
var result = document.frmmain.objFP.ErrorCode;
if (result == 0) {
//var strimg1 = objFP.ImageTextData;
var strmin = document.frmmain.objFP.MinTextData;
//document.frmmain.min.value = strmin;
document.frmdata.Thumb.value = strmin;
}
else
alert('Failed during captured - ' + result);
return;
}
And then I am passing document.frmdata.Thumb to server side to a webservice. But webservice provider are saying that "you are sending an invalid base64"
There is a property like
document.frmdata.Thumb.ContentType
But I am not sure how to send this in base64.
Any help will be apprecited
If the data is a string you could use the btoa() function on the window object:
console.log(document.frmdata.Thumb.value);
> "088BA76AFE122" Some raw string value from scanner
window.btoa(thumb);
> "MDg4QkE3NkFGRTEyMg==" Base-64 encoded string

Converting byte array to jpeg image in javascript

I have an asp.net page.
Inside this page I have an img control/element.
I am calling an ashx page on my server.
This ashx page accepts a timestamp from the client and compares it to a timestamp stored on the server.
If the timestamps do not match then I return an image which has been converted to a byte array (in C#).
If the timestamps do not match then I return a string value of "-1".
So, this is a cut-down of my ashx page:
public void ProcessRequest (HttpContext context) {
context.Response.AddHeader("Access-Control-Allow-Origin", "*");
try
{
string clientTS = context.Request.QueryString["clientTS"];
if (clientTS == serverTS)
{
//new version available. refresh browser
context.Response.ContentType = "text/json";
string value = "-1";
context.Response.Write(value);
}
else
{
context.Response.ContentType = "image/jpg";
byte[] data = Shared.GetMobileNextFrame("par1", 0);
context.Response.BinaryWrite(data);
}
}
catch (Exception ex)
{
context.Response.ContentType = "text/json";
context.Response.Write("ERR");
}
}
And in my javascript code:
function GetImageStatus() {
finished = false;
var val = url + '/Mobile/isNewFrame.ashx?Alias=' + Alias + '&CamIndex=' + camIndex + '&Version=' + version + '&GuidLogOn=' + guidLogOn;
$.ajax({
url: val,
type: 'GET',
timeout: refreshWaitlimit,
data: lastTS,
success: function (response, status, xhr) {
var ct = xhr.getResponseHeader("content-type");
if (ct.indexOf('json') > -1) {
//no update
}
else {
try {
live1x4.src = 'data:image/bmp;base64,' + encode(response);
}
catch (err) {
alert(err);
}
}
},
error: function (jqXHR, textStatus, errorThrown) {
//handle error
}
});
}
function encode(data) {
var str = String.fromCharCode.apply(null, data);
return btoa(str).replace(/.{76}(?=.)/g, '$&\n');
}
But I get an error returned:
TypeError: Function.prototype.apply: Arguments list has wrong type
If I just apply:
live1x4.src = 'data:image/bmp;base64,' + btoa(response);
instead of:
live1x4.src = 'data:image/bmp;base64,' + encode(response);
I get this error:
InvalidCharacterError: btoa failed. the string to be encoded contains
characters outside of the Latin1 range.
I have tried using a canvas control with example code i have found on this site. I do not get an error but I also do not get an image.
I know the image is valid because my old code was point the image.src directly to the ashx handler (and i was not comparing timestamps).
I do not want to encode the byte array to base64 string on the server because that would inflate the download data.
I was wondering if I was using the wrong context.Response.ContentType but I could not see what else I could use.
What am i doing wrong?
When looking at the documentation at MDN you should pass 1 or more parameters to fromCharCode. You pass none in this line:
var str = String.fromCharCode.apply(null, data);
The syntax is:
String.fromCharCode(num1, ..., numN)
There is although the apply method as your said in comments, but you use it the wrong way. The first parameter shouldn't be null.
The syntax of that is (from Convert array of byte values to base64 encoded string and break long lines, Javascript (code golf)):
somefunction.apply(thisObj[, argsArray])
Just use
var str = String.fromCharCode.apply(data);
So use the thisObj parameter to pass the data.

Responding with a JSON object in Node.js (converting object/array to JSON string)

I'm a newb to back-end code and I'm trying to create a function that will respond to me a JSON string. I currently have this from an example
function random(response) {
console.log("Request handler 'random was called.");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("random numbers that should come in the form of json");
response.end();
}
This basically just prints the string "random numbers that should come in the form of JSON". What I want this to do is respond with a JSON string of whatever numbers. Do I need to put a different content-type? should this function pass that value to another one say on the client side?
Thanks for your help!
Using res.json with Express:
function random(response) {
console.log("response.json sets the appropriate header and performs JSON.stringify");
response.json({
anObject: { item1: "item1val", item2: "item2val" },
anArray: ["item1", "item2"],
another: "item"
});
}
Alternatively:
function random(response) {
console.log("Request handler random was called.");
response.writeHead(200, {"Content-Type": "application/json"});
var otherArray = ["item1", "item2"];
var otherObject = { item1: "item1val", item2: "item2val" };
var json = JSON.stringify({
anObject: otherObject,
anArray: otherArray,
another: "item"
});
response.end(json);
}
var objToJson = { };
objToJson.response = response;
response.write(JSON.stringify(objToJson));
If you alert(JSON.stringify(objToJson)) you will get {"response":"value"}
You have to use the JSON.stringify() function included with the V8 engine that node uses.
var objToJson = { ... };
response.write(JSON.stringify(objToJson));
Edit: As far as I know, IANA has officially registered a MIME type for JSON as application/json in RFC4627. It is also is listed in the Internet Media Type list here.
Per JamieL's answer to another post:
Since Express.js 3x the response object has a json() method which sets
all the headers correctly for you.
Example:
res.json({"foo": "bar"});
in express there may be application-scoped JSON formatters.
after looking at express\lib\response.js, I'm using this routine:
function writeJsonPToRes(app, req, res, obj) {
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
res.set('Content-Type', 'application/json');
var partOfResponse = JSON.stringify(obj, replacer, spaces)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
var callback = req.query[app.get('jsonp callback name')];
if (callback) {
if (Array.isArray(callback)) callback = callback[0];
res.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
partOfResponse = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + partOfResponse + ');\n';
}
res.write(partOfResponse);
}
const http = require('http');
const url = require('url');
http.createServer((req,res)=>{
const parseObj = url.parse(req.url,true);
const users = [{id:1,name:'soura'},{id:2,name:'soumya'}]
if(parseObj.pathname == '/user-details' && req.method == "GET") {
let Id = parseObj.query.id;
let user_details = {};
users.forEach((data,index)=>{
if(data.id == Id){
user_details = data;
}
})
res.writeHead(200,{'x-auth-token':'Auth Token'})
res.write(JSON.stringify(user_details)) // Json to String Convert
res.end();
}
}).listen(8000);
I have used the above code in my existing project.
The JSON.stringify() method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.
response.write(JSON.stringify({ x: 5, y: 6 }));
know more

Categories