Proper way to deal with variations in JSON serialization - json

I have a web service that uses Python's SimpleJSON to serialize JSON, and a javascript/ client that uses Google's Visualization API. When I try to read in the JSON response using Google Data Table's Query method, I am getting a "invalid label" error.
I noticed that Google spreadsheet outputs JSON without quotes around the object keys. I tried reading in JSON without the quotes and that works. I was wondering what was the best way to get SimpleJSON output to be read into Google datable using
query = new google.visualization.Query("http://www.myuri.com/api/").
I could use a regex to remove the quotes, but that seems sloppy. The javascript JSON parsing libraries I've tried won't read in JSON syntax without quotes around the object keys.
Here's some good background reading re: quotes around object keys:
http://simonwillison.net/2006/Oct/11/json/.

Are you certain the Google API is expecting JSON? In my experience Google's APIs tend not to be massively broken in a manner you're describing -- it could be that they're actually expecting a different format that merely resembles JSON.
Further poking around reveals instructions for retrieving data in the format Google expects:
For example, to get the dataSourceUrl
from a Google Spreadsheet, do the
following:
In your spreadsheet, select the range of cells.
Select 'Insert' and then 'Gadget' from the menu.
Open the gadget's menu by clicking on the top-right selector.
Select menu option 'Get data source URL'.
I did this and opened the URL in my browser. The data it was returning was certainly not JSON:
google.visualization.Query.setResponse(
{requestId:'0',status:'ok',signature:'1464883469881501252',
table:{cols: [{id:'A',label:'',type:'t',pattern:''},
{id:'B',label:'',type:'t',pattern:''}],
rows: [[{v:'a'},{v:'h'}],[{v:'b'},{v:'i'}],[{v:'c'},{v:'j'}],[{v:'d'},{v:'k'}],[{v:'e'},{v:'l'}],[{v:'f'},{v:'m'}],[{v:'g'},{v:'n'}]]}});
It looks like the result is intended to be directly executed by the browser. Try modifying your code to do something like this:
# old
return simplejson.dumps ({"requestId": 1, "status": "ok", ...})
# new
json = simplejson.dumps ({"requestId": 1, "status": "ok", ...})
return "google.visualization.Query.setResponse(%r);" % json

The "invalid label" error is usually due to a blind eval() on the JSON string, resulting in property names being mistaken as labels (because they have the same syntax -- "foo:").
eval("{ foo: 42, bar: 43 }"); // Results in invalid label
The quick remedy is to make sure your JSON string has parenthesis enclosing the curly braces:
eval("({ foo: 42, bar: 43 })"); // Works
Try enclosing your JSON string in parenthesis to see if the "invalid label" error goes away.

As it turns out :mod:json would also choke at strings in single quotes. This will sort things out though:
Parse JavaScript object as JSON in python:
solution:
>>> from re import sub
>>> import json
>>> js = "{ a: 'a' }"
>>> json.loads(sub("'", '"', sub('\s(\w+):', r' "\1":', js)))
{u'a': u'a'}
Edit: (edge cases reviewed)
So it was brought up that the suggested solution would not cope with all cases and specifically with something like
e.g. {foo: "a sentence: right here!"} will get changed to {"foo": "a "sentence": right here!"}
– Jason S Apr 12 at 18:03
To resolve that we simply need to ensure that we are in fact working with a key and not simply a colon in a string so we do a little look behind magic to hint at a comma(,) or a curly brace({) presence to ensure we have it proper, like so:
colon in string:
>>> js = "{foo: 'a sentence: right here!'}"
>>> json.loads(sub("'", '"', sub('(?<={|,)\s*(\w+):', r' "\1":', js)))
{u'foo': u'a sentence: right here!'}
Which of course is the same as doing:
>>> js = "{foo: 'a sentence: right here!'}"
>>> json.loads(sub('(?<={|,)\s*(\w+):', r' "\1":', js).replace("'",'"'))
{u'foo': u'a sentence: right here!'}
But then I pointed out that this is not the only flaw because what about quotes:
If we are also concerned about escaped quotes we will have to be slightly more specific as to what constitutes a string. The first quote will follow either a curly brace({) a space(\s) or a colon(:) while the last matching quote will come before either a comma(,) or a closing curly brace(}) then we can consider everything in between as part of the same string, like so:
additional quotes in string:
>>> js = "{foo: 'a sentence: it\'s right here!'}"
>>> json.loads(
... sub("(?<=\s|{|:)'(.*?)'(?=,|})",
... r'"\1"',
... sub('(?<={|,)\s*(\w+):', r' "\1":', js))
... )
{u'foo': u"a sentence: it's right here!"}
Watch this space as more edge cases are revealed and solved. Can you spot another?
Or for something more complex perhaps, a real world example as returned by npm view:
From:
{ name: 'chuck',
description: 'Chuck Norris joke dispenser.',
'dist-tags': { latest: '0.0.3' },
versions: '0.0.3',
maintainers: 'qard ',
time: { '0.0.3': '2011-08-19T22:00:54.744Z' },
author: 'Stephen Belanger ',
repository:
{ type: 'git',
url: 'git://github.com/qard/chuck.git' },
version: '0.0.3',
dependencies: { 'coffee-script': '>= 1.1.1' },
keywords:
[ 'chuck',
'norris',
'jokes',
'funny',
'fun' ],
bin: { chuck: './bin/chuck' },
main: 'index',
engines: { node: '>= 0.4.1 < 0.5.0' },
devDependencies: {},
dist:
{ shasum: '3af700056794400218f99b7da1170a4343f355ec',
tarball: 'http://registry.npmjs.org/chuck/-/chuck-0.0.3.tgz' },
scripts: {},
directories: {},
optionalDependencies: {} }
To:
{u'author': u'Stephen Belanger ',
u'bin': {u'chuck': u'./bin/chuck'},
u'dependencies': {u'coffee-script': u'>= 1.1.1'},
u'description': u'Chuck Norris joke dispenser.',
u'devDependencies': {},
u'directories': {},
u'dist': {u'shasum': u'3af700056794400218f99b7da1170a4343f355ec',
u'tarball': u'http://registry.npmjs.org/chuck/-/chuck-0.0.3.tgz'},
u'dist-tags': {u'latest': u'0.0.3'},
u'engines': {u'node': u'>= 0.4.1 < 0.5.0'},
u'keywords': [u'chuck', u'norris', u'jokes', u'funny', u'fun'],
u'main': u'index',
u'maintainers': u'qard ',
u'name': u'chuck',
u'optionalDependencies': {},
u'repository': {u'type': u'git', u'url': u'git://github.com/qard/chuck.git'},
u'scripts': {},
u'time': {u'0.0.3': u'2011-08-19T22:00:54.744Z'},
u'version': u'0.0.3',
u'versions': u'0.0.3'}
Works for me =)
nJoy!

Related

Google Apps Script API Request with GraphQL parse error

I'm trying to make API request with using Apps Script. API is GraphQL based.
I'm using JSON.stringify function. But API returns an error.
Request:
payload={"query":"{mutation {change_column_value (board_id: 177955, item_id: 287466289, column_id:\"phone\", value: \"{phone : 15065332974, countryShortName: \"US\" }\") {name column_values{ id value}}}"}}
I'm getting error;
{"errors":[{"message":"Parse error on \" }\" (STRING) at [1, 148]","locations":[{"line":1,"column":148}]}]}
Apps Script Code:
var us = "US"
var column_values = '{ \
mutation {change_column_value (board_id: 177955, item_id: '+ 287466289 +', column_id:"phone", value: "{phone : 15065332974, countryShortName: \"' + us +'\" }") {name column_values{ id value}} \
}';
settings = {
"method": "POST",
"headers": {"Content-Type" : "application/json","Authorization" : "eyJhbGciOiJIXXXXXXXXXX"},
"payload" : JSON.stringify({query:column_values})
}
response= UrlFetchApp.fetch(url, settings);
Brackets in a GraphQL operation indicate a selection set -- one or more fields that are being requested. The operation itself (whether a query or a mutation) is not a field, so you don't wrap the whole operation in brackets.
Correct:
mutation {
doSomething
}
Incorrect:
{
mutation {
doSomething
}
}
The only time you will see brackets that look like they are on the "outside" is when using query shorthand.
Provided that value is a String, then your use of backslashes to escape the double quotes inside that string should be fine.
You might also consider using variables, which would clean up your code considerably and make it less error prone.
You do not need to escape special characters inside a JSON request.
The syntax implies using key-value pairs assigned to each other with a colon, whereby strings are located inside quotes (not escaped with a backslash!), while numbers and variable names are not.
The brackets should be used in function of your desired API request as documented.

Power App - response from Flow is not treated as json

I have a power app that wants to collect some data from a rest API, I am using Flow as this seems to be the recommended way.
I have a collection:
[
{id: 1,name: "test",lines: [
{id: 244,
StrategyId: 1,
TypeId: 0,
Weight: 10,
toWeight: 200
}
]
},
{id: 3,name: "test2",lines: [
{id: 262,
StrategyId: 3,
TypeId: 0,
Weight: 0,
toWeight: 200
}
]
}
]
When I enter this into a formula straight up, it works fine:
ClearCollect( CityPopulations, json...)
However, when I try to fetch the data from Flow it only returns a collection with one single entry that is the json data as string.
Set('CityPopulations','PowerApp->Sendmeamobilenotification'.Run());ClearCollect(NewTable, CityPopulations.testoutput);
In Microsoft flow I use PowerApps trigger and Respond to PowerApps.
Also note that there are no " on the propertynames in json structure, but this was the only way I could get Power Apps to actually accept typing in json manually. I have tried having the respons propper, as well as this way. I had to adjust the flow to remove the " around propertynames. Still same result.
This is how the table looks in collections:
This is how it looks when I am entering the data manually:
this is the direct output from :
{
"testoutput": "[{id:1,name:\"test\",lines:[{id:244,StrategyId:1,TypeId:0,fromWeight:10,toWeight:200}]},{id:2,name:\"tester\",lines:[{id:154,StrategyId:2,TypeId:0,fromWeight:10,toWeight:200}]}]"
}
The biggest difference is the escape characters added from flow.
This took me quite some time to solve so maybe will help others.
When you want to return an array (collection) form Flow to a Canvas App you need to use Response (Request), not Respond to PowerApps.
All the problems go away with that
Tip: Remember to provide a SCHEMA and to remove and re-add the flow in the canvas app
It looks like your Collecting a single column of Text from the API Response rather than collecting the entire response.
Try just using:
ClearCollect(colNewTable,'PowerApp->Sendmeamobilenotification'.Run())
If you want to set colNewTable as a variable, wrap it like:
Set(varNewTable,ClearCollect(colNewTable,'PowerApp->Sendmeamobilenotification'.Run())
Hope this helps!

Could not parse request body into json: Unexpected character (\'-\' (code 45)) AWS Lambda + API + Postman

I have been trying for a few days to get a parameter sent from the API Gateway in AWS to a Lambda function and I am having no success.
I decided to start from the beginning so I followed their walkthrough (http://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started.html#getting-started-new-lambda)
I have checked this walkthrough twice and I have followed the steps to the letter.
Problem
When I test the API from Postman or in Swift I am getting the error:
{"message": "Could not parse request body into json: Unexpected character (\'-\' (code 45)) in numeric value: expected digit (0-9) to follow minus sign, for valid numeric value\n at [Source: [B#c036d15; line: 1, column: 3]"}
In postman, When I view the result as JSON I just get
Bad String
Lambda Function
The function is the basic example from the Walkthrough:
console.log('Loading event');
exports.handler = function(event, context) {
var name = (event.name === undefined ? 'No-Name' : event.name);
console.log('"Hello":"' + name + '"');
context.done(null, {"Hello":name}); // SUCCESS with message
};
When Tested from the Lambda Console and with the Test data I get the result:
{
"Hello": "TestUser123"
}
When Tested from the API Gateway Test, The result is also:
{
"Hello": "TestUser123"
}
Can anyone see why both test consoles are allowing this work but when tested with POSTMAN or used within a Swift Script it does not work ?
Edit 1
In postman, I have set the content-type to application/json
The script returns the default:
{
"Hello": "user"
}
However, When I add in the parameters name and TestUser123 in POSTMAN, this is when it returns the error.
Update 1
Ok, so I changed the mapping template to one that I found on another answer:
{ "name": "$input.params('name')" }
Now the result is:
{
"Hello": ""
}
Any Ideas why it is not getting the name?
I just got stuck with this today.
your mapping template is:
{ "name": "$input.params('name')" }
AWS uses AWS Velocity templates, which; even though looks like JSON, is different.
if you use
{ "name": $input.params('name') } // notice no quotes
for the mapping template right at the integration request step, then it should work as expected.
Read the error message very carefully, it actually tells you the problem. For example, I got
Could not parse request body into json: Unexpected character (\'\"\' (code 34)): was expecting comma to separate Object entries
So the problem is that I'm missing a comma. I check my Lambda's Integration Request - Body Mapping Template:
{
"age" : $input.json('$.persondata.age'),
"income" : $input.json('$.persondata.income')
"height" : $input.json('$.persondata.height')
}
Can you spot the problem? I am missing a comma after the income line.
Here is another example.
Could not parse request body into json: Unexpected character (\'}\' (code 125)): expected a value
When I look at the Integration Request - Body Mapping Template:
#set($inputRoot = $input.path('$'))
{
"age" : $inputRoot.age,
"height" : $inputRoot.height,
"income" : $inputRootincome
}
Can you spot the problem? I am missing a dot in $inputRootincome.
Error Message :
Could not parse request body into json: Could not parse payload into json: Unrecognized token \' \': was expecting (\'true\', \'false\' or \'null\')
Cause of the error : When string values inside the json are not assigned using double quotations in the aws mapping template the error occurs.
Solution : (Please Note : This example is for application/json type request template)
Actually the solution for the problem is, if you are using a value of type string in json then its value should be assigned inside a ("" - double quotation marks) in the mapping template.
The below shown example has the following attributes :
customerId - string (Please note : this value comes from a query parameter)
customerName - string
customerAge - integer
isPermanentEmployee - boolean
customerAddress - string (Please note this is an optional parameter)
And the mapping template should be defined like the example shown below
Refer the example below :
#set($inputRoot = $input.path('$'))
{
"CustomerId": "$input.params('customerId')",
"CustomerName": "$inputRoot.customerName",
"CustomerAge": $inputRoot.customerAge,
"IsPermanentEmployee": $inputRoot.isPermanentEmployee
"CustomerAddress ": #if($inputRoot.customerAddress == "") "" #elseif($inputRoot.customerAddress != "") "$inputRoot.customerAddress" #end
}
If you note the above mapping template, I would have given string values inside double quotation marks("") which will solve the error
Also this example contains how to handle optional parameters in aws mapping templates using #if#else statements.
It is likely that you had copy-pasted multiple lines in your "Integration Request" in the API gateway.
When copying a line and pasting it below, you might have copied the hidden character '\n' at the end of that line. This is probably causing issues at the lambda function.
Example: Copying the line containing age and pasting it twice and modifying them to have height and income respectively.
#set($inputRoot = $input.path('$'))
{
"age" : $inputRoot.age,
"height": $inputRoot.height,
"income": $inputRoot.income
}
Instead of copy-pasting, just type the line out for height and income.

DotNet.Highcharts and proper JSON

I have an inconsistent issue with the JSON sent to highcharts. Highcharts does not always reject the JSON, but when it does, updating the JSON by hand to a proper format consistently fixes it:
Exmaple DotNet.Hightcharts output:
series: [{ data: [284, 49, 100, 19], name: 'some name' }, { data: [230, 250, 219, 878], name: 'some name 2' }]
when fixed to included quotes it works:
"series": [{ "data": [284, 49, 100, 19], "name": "some name" }, { "data": [230, 250, 219, 878], "name": "some name 2" }]
Is there a way to get DotNet.Hightcharts to output this format?
Looking at the DotNet.Highcharts source code, it seems it uses its own JsonSerializer which does not quote property names, and there's no option to change this behavior nor swap out the serializer for another one altogether. They have made it straight forward to change the formatting in code though, so it looks like changing the following lines in: DotNet.Highcharts/JsonSerializer.cs should do what you need:
const string JSON_PROPERTY_WITH_VALUE_FORMAT = "{0}: {1}";
const string JSON_STRING_FORMAT = "'{0}'";
to become:
const string JSON_PROPERTY_WITH_VALUE_FORMAT = "{\"0\"}: {1}";
const string JSON_STRING_FORMAT = "\"{0}\"";
So I'd say you options are:
Compile your own version of DotNet.Highcharts using the source code with the above alterations.
Convince the project developers to include such changes in the next release.
Use an alternative library such as Highcharts.Net which does quote names by default.
Use no library at all and just render your data into a literal placed inside the hand-coded Highcharts javascript, using a stand alone Json formatter like Newtonsoft.Json.
Before any of the above though, it does sound a bit odd that this problem only emerges as more data is added. It's not just a case of apostrophes in your data breaking the format is it? They don't appear to be being escaped in the formatter.

Ajax JSON Parse Error

I find this kinda strange. it works on older versions of iQuery (ie 1.2.x to 1.3). However, i am getting a parserror when using jQuery 1.4.x
Any help will be appreciated. Thank you.
This is what i get from XMLHttpRequest.responseText
({count: 5, success: true, error: '', cache: false, data: [{column: ['Mike','Mike','Steve','Steve','Steve']}]})
Jquery 1.4 updated their JSON parser, so that it no longer accepts invalid JSON that it did before. Your server is outputting invalid JSON that was previously tolerated, but no longer is. This change was mentioned (briefly) in the release notes and documentation, and there's been lots of discussion about it if you google "jquery 1.4 JSON"
But to sum up: the keys in JSON, to be valid, must have quotes around them, the string can't be surrounded by those parentheses, and nothing can use single quotes as delimiters, so your JSON would need to be:
{"count": 5, "success": true, "error": "", "cache": false, "data": [{"column": ["Mike","Mike","Steve","Steve","Steve"]}]}
or, more readably:
{
"count": 5,
"success": true,
"error": "",
"cache": false,
"data": [
{
"column": [
"Mike",
"Mike",
"Steve",
"Steve",
"Steve"
]
}
]
}
The specs are very nicely and clearly spelled out at http://www.json.org/ with pictures and diagrams. You can also check your JSON at JSONLint - the JSON above checks out there just fine, but your original JSON raises all kinds of errors.
So as to what you need to do: if you have the ability to change what your server sends out, do that - make it output valid JSON. Please. Only if you are unable to change the server's behavior, you'll have to use a workaround from one of the posts linked above, or use some other JSON library.
General principle: in all cases, doing it the Right Way™ is preferable - fixing the server's output (even if that's filing a bug on an external server/API) is the long-term solution, but may not be immediately practical. So hackish workarounds are sometimes necessary, but not optimal.