Why is Google Scripts' JSON.parse method converting numbers to floats? - javascript

I'm writing a Google Script code to manage some data in Google Sheet. I'm pulling data from external API and every time I receive numbers in JSON response they're converted to floats by JSON.parse available in Google Script.
Example:
const test = '{ "numericValue": 0 }';
JSON.parse(test)
Result: {numericValue=0.0}
const test = '{ "stringValue": "0" }';
JSON.parse(test)
Result: {stringValue=0}
It doesn't happen with browser JSON.parse method.
Is there any way to fix this besides converting all numbers to strings before parsing?

The logger has always done that. But the console doesn't. Don't know the issue but you get use to it. Don't worry about it
function floatTest() {
const obj='{"value": 0 }';
console.log(JSON.parse(obj));
Logger.log(JSON.parse(obj));
}
Execution log
8:28:00 AM Notice Execution started
8:28:00 AM Info { value: 0 }
8:28:00 AM Info {value=0.0}
8:28:01 AM Notice Execution completed

a simple way to verify that what you are saying is wrong:
const test_1 = JSON.parse( '{ "val": 0 }')
console.log ('test_1', typeof test_1.val, Number.isInteger(test_1.val) , test_1.val )
const test_2 = JSON.parse( '{ "val": "0" }')
console.log ('test_2', typeof test_2.val, Number.isInteger(test_2.val) , test_2.val )

Related

I get an object as a string. How to convert?

Using IMAP I receive an email.
But parsing, I get this:
{
from: [ "name lastname <mygmail#gmail.com>" ],
date: [ "Mon, 21 Jun 2021 13:41:51 +0500" ],
subject: [ "hello" ]
}
The catch is that this is a string, not an object at all!
I cannot take advantage of this.
How do I convert this string to an object?
JSON.parse() throws an error message:
UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token f in JSON at position 141
You get that error because in JSON notation properties should be enclosed in double quotes like this:
{
"from": [ "name lastname <mygmail#gmail.com>" ],
"date": [ "Mon, 21 Jun 2021 13:41:51 +0500" ],
"subject": [ "hello" ]
}
So, if what you get is a string like what you showed, you'll have to add these double quotes yourself (maybe with a nice and coolio regex)
As others have noted, because this is not JSON, you can't parse it as JSON. What it does appear to be is a JavaScript snippet.
In the past eval(customJavaScriptCode) would have been used for this but it is very insecure so avoid it.
A better and safer (but not bulletproof) way is to run the JavaScript in a sandbox environment. It's hard to break out of that. You do that like this:
Prefix your object with result = , the name doesn't really matter.
Execute the JavaScript in a sandbox.
Get the value of the last statement.
const vm = require('vm');
const script = new vm.Script('result = ' + `{
from: [ "name lastname <mygmail#gmail.com>" ],
date: [ "Mon, 21 Jun 2021 13:41:51 +0500" ],
subject: [ "hello" ]
}`);
const lastStatementResult = script.runInNewContext();
console.log(lastStatementResult.subject[0] == "hello");
You now have the object parsed as JS in a relatively safe way.
If you need more than just the last statement, you can do this like this:
const vm = require('vm');
const script = new vm.Script(`
var foo = "bar";
var baz = foo + "123";
`);
const context = {}; // will contain global variables from the script
script.runInNewContext(context);
console.log(context.foo + '123' === context.baz);
I was also running into this issue following the documentation to the letter. The problem is exactly how OP describes, there is a return value from one of Imap's callback functions that is shaped like an object, but does not respond to dot notation, nor JSON.parse/stringify.
The problem was using the suggested inspect property from the util node package. This is what is returning a string shaped like an object that is unusable. Instead, omit the inspect on the values you need to parse.
Original usage (straight from the docs) (bad)
stream.once('end', function() {
console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
});
My solution (good)
stream.once('end', function() {
console.log(prefix + 'Parsed header: %s', Imap.parseHeader(buffer));
});
All I did was omit the inspect. I can now assign Imap.parseHeader(buffer) to a variable and manipulate it just like a normal JS object.
I did not understand your question correctly.
Try this
JSON.stringify(JSON.parse(Object))
If error exist, Please describe your issue with more

Is it possible to make a freebusy query to the Google Calendar API with a template literal in the request body?

var freeRequest = gapi.client.calendar.freebusy.query({
"items": [
{"id": calendarid}
],
"timeMin": `"${date.value}T${startTime.value}:00.000Z"`,
"timeMax": `"${date.value}T${endTime.value}:00.000Z"`,
"timeZone": "GMT+01:00",
});
I want to check if a calendar is busy via the freebusy query
I know the request only works with the date format: YYYY-MM-DDTHH:MM:SS.MMMZ .
My plan was to paste the value that I get from an html input into a string literal and then format it accordingly. When I console.log:
`"${date.value}T${endTime.value}:00.000Z"`
The console gives me the date in the right format (e.g. "2021-03-31T18:29:00.000Z"). But when sending the request in my application it gives me a 400 bad request error. I guess the problem lies in the string literal because the timeMin and timeMax values should only have quotation marks around it and not the
``
I also tried saving the date in a variable, but this also did not work.
Is there a way to solve this?
I was able to replicate your issue using Apps Script since it also uses Javascript. Your findings are correct, when you use the template literals, the request parameter timeMin and timeMax becomes a string rather than a datetime format. What I do was to concatenate the string using (+) operator and it works.
Sample Code:
var calendarId = "sample#group.calendar.google.com";
var date = {
value: "2021-04-03"
}
var startTime = {
value: "01:00"
}
var endTime = {
value: "05:00"
}
// Bad request
var resource1 = {
timeMin: `"${date.value}T${startTime.value}:00.000Z"`,
timeMax: `"${date.value}T${endTime.value}:00.000Z"`,
items: [
{
id: calendarId
}
],
timeZone: "GMT+08:00"
};
Logger.log(resource1);
// Successful request
var resource2 = {
timeMin: date.value+"T"+startTime.value+":00.000Z",
timeMax: date.value+"T"+endTime.value+":00.000Z",
items: [
{
id: calendarId
}
],
timeZone: "GMT+08:00"
};
Logger.log(resource2);
var request = Calendar.Freebusy.query(resource2);
Logger.log(request);
Output:
6:38:55 AM Notice Execution started
6:38:56 AM Info {items=[{id=sample#group.calendar.google.com}], timeMax="2021-04-03T05:00:00.000Z", timeMin="2021-04-03T01:00:00.000Z", timeZone=GMT+08:00}
6:38:56 AM Info {items=[{id=sample#group.calendar.google.com}], timeMax=2021-04-03T05:00:00.000Z, timeMin=2021-04-03T01:00:00.000Z, timeZone=GMT+08:00}
6:38:56 AM Info {timeMax=2021-04-03T05:00:00.000Z, calendars={sample#group.calendar.google.com={busy=[]}}, kind=calendar#freeBusy, timeMin=2021-04-03T01:00:00.000Z}
6:38:57 AM Notice Execution completed
Notice that the timeMin and timeMax becomes a string in the resource1 variable while in resource2 they are not in string format

Parsing a JSON sub string

I have javascript function that calls an external Api and returns in most case a valid JSON string.
function (successResponse) {
{
console.log(successResponse);
}
However, in some cases it return the the following invalid JSON
Response: Status=200, Text: {"createdTime":"2017-05-08T14:47:56Z","lastUpdatedTime":"2017-05-08T14:47:56Z","createdMode":"API","uuid":"e333c1-3599-36d7-9ef5-dc22c79a4a52","userId":"anonymous"}, Error Message: null
How can I parse the above string to get the 'uuid'
Thanks
If you're expecting a response string in that format, you can use a regular expression to extract the "text" portion of the response:
function (successResponse) {
{
var responseText = successResponse.match(/\{.+\}/);
var responseTextJSON = JSON.parse(responseText);
var uuid = responseTextJSON.uuid;
console.log(uuid);
}
Maybe you can parse the string yourself to exclude everything outside of {} ?
var apiResponse = 'Response: Status=200, Text: {"createdTime":"2017-05-08T14:47:56Z","lastUpdatedTime":"2017-05-08T14:47:56Z","createdMode":"API","uuid":"e333c1-3599-36d7-9ef5-dc22c79a4a52","userId":"anonymous"}, Error Message: null';
var apiResponse_fixed = apiResponse.substring((apiResponse.indexOf("{") - 1), (apiResponse.lastIndexOf("}") + 1));
var json_obj = JSON.parse(apiResponse_fixed);
console.log(json_obj.uuid);
Replace the non-JSON features, and then interpret as JSON
Looks like the server owner has been a bit lazy, and programmed an error response which contains a JSON-like interior section but surrounded by a couple of non-JSON elements.
If you are desperate to resolve the situation and have no ability to fix the server output format, here is my suggestion:
notQuiteJson = 'Response: Status=200, Text: {"createdTime":"2017-05-08T14:47:56Z","lastUpdatedTime":"2017-05-08T14:47:56Z","createdMode":"API","uuid":"e333c1-3599-36d7-9ef5-dc22c79a4a52","userId":"anonymous"}, Error Message: null';
madeJson = notQuiteJson.replace('Response: Status=200, Text:','{"Response": {"Status":200}, "Text":').replace('Error Message: null','"ErrorMessage": null}')
obj = JSON.parse(madeJson)
console.log(obj.Text.uuid) // Result: "e333c1-3599-36d7-9ef5-dc22c79a4a52"
Of course this only works if the error message is always exactly this. In reality you may want to use a 3-digit wildcard to cover a range of "Status=" codes. But then you would have to also be confident that all the error modes produce the same non-JSON text at the start and end of the response.
Disclaimer
#sp00m and #Bergi, don't kill me: you are right of course, but this is just for if the poster has no choice in the matter 8-)

JSON string assignment not working

What is wrong with my code below? I am passing to this function a JSON string but the assignment ends up blank. I can see the string in FireBug in the function parameter so I know it arrives at the function, but it does not make it past the assignment (as in the assignment is "undefined"). I want to pull from the string the wty_upgrade array. I am using some of the code I received help with in this question. Thanks
JSON string (as I see it in FireBug:
{"wty_upgrade":[
{"wty":"Upgrade from 3 to 5 years", "mth":24, "pig":3000},
{"wty":"Upgrade from 3 to 10 years", "mth":84, "pig":8000}
]}
Function Call:
LoadWtyUpgPlans('3', JSON)
function LoadWtyUpgPlans(StdWty, UpgWty) {
var WtyRow = '';
var WtyUpgrades = UpgWty.wty_upgrade; <--- here the assignment is blank/undefined
STUFF HERE...
};
you can use as a this example may be useful fir you,
var jsn = '{"wty_upgrade":[
{"wty":"Upgrade from 3 to 5 years", "mth":24, "pig":3000},
{"wty":"Upgrade from 3 to 10 years", "mth":84, "pig":8000}]}';
//OR you can parse then use
jsonres = JSON.parse(jsn);

jsonpath: add 2 elements and then compare

Given the following json data:
[
{
"item":"T1",
"size":10,
"offset":3
},
{
"item":"T2",
"size":20,
"offset":5
}
]
query expr: $.[? ((#size + #offset)<15)].item
the query returns nothing. Anything wrong? it looks like (#size + #offset) is not supported. If so, what is the correct syntax?
I think you might need to try this instead:
$.[? ((#.size + #.offset)<15)].item
Note the dot after each # sign.
With DefiantJS, you can execute XPath on JSON structures.
http://defiantjs.com/
At this site (http://defiantjs.com/#xpath_evaluator), you can paste in your JSON data and try out standard XPath. I have tested your json data and tested this XPath expression, which works as expected:
//*[size + offset > 15]/item
DefiantJS extends the global object JSON with an additional method; "search" and in javascript, it looks like this:
var res = JSON.search(json_data, "//*[size + offset > 15]/item");
console.log(res[0]);
// T2

Categories

Resources