Second question for the day :)
Still working on my first ever app, and I've hit a bit of a snag using an API that returns currency exchange values. I need to extract the current AUD value from this JSON :
{"base":"USD","date":"2016-05-30","rates":{"AUD":1.3919,"BGN":1.7558,"BRL":3.6043,"CAD":1.3039,"CHF":0.99273,"CNY":6.5817,"CZK":24.258,"DKK":6.6765,"GBP":0.68341,"HKD":7.7688,"HRK":6.7195,"HUF":281.72,"IDR":13645.0,"ILS":3.8466,"INR":67.139,"JPY":111.19,"KRW":1190.9,"MXN":18.473,"MYR":4.1175,"NOK":8.3513,"NZD":1.4924,"PHP":46.73,"PLN":3.9447,"RON":4.0428,"RUB":65.89,"SEK":8.3338,"SGD":1.3811,"THB":35.73,"TRY":2.9565,"ZAR":15.771,"EUR":0.89775}}
Here is the code I am using:
var http = require('http');
var options = {
host: 'api.fixer.io',
port: 80,
path: '/latest?base=USD',
method: 'GET'
};
http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function (chunk) {
const json = JSON.parse(chunk);
rate = json.AUD;
console.log(rate);
});
}).end();
Unfortunately this doesn't work, and I assume that is because the JSON is nested? How do I go about querying this nested string correctly?
I also know I need to tighten up my handling of chunks, but it's baby steps for me right now :)
Thank you!
not json.AUD, it is
json.rates.AUD
You should wait for whole data first, or use one of the streaming parsers instead (for example: https://github.com/dominictarr/JSONStream).
That is because "chunk" is not all data at once - it may be just part of it, which means it's not a valid JSON itself.
http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
var data = '';
res.setEncoding('utf8');
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
const json = JSON.parse(data);
// As #huaoguo mentioned, it should be `json.rates.AUD`, not `json.AUD`
rate = json.rates.AUD;
console.log(rate);
});
}).end();
Also, as #huaoguo mentioned, there should be json.rates.AUD instead of json.AUD.
Related
I am working in NodeJS with CouchDB 2.1.1.
I'm using the http.request() method to set various config settings using the CouchDB API.
Here's their API reference, yes, I've read it:
Configuration API
Here's an example of a working request to set the logging level:
const http = require('http');
var configOptions = {
host: 'localhost',
path: '/_node/couchdb#localhost/_config/',
port:5984,
header: {
'Content-Type': 'application/json'
}
};
function setLogLevel(){
configOptions.path = configOptions.path+'log/level';
configOptions.method = 'PUT';
var responseString = '';
var req = http.request(configOptions, function(res){
res.on("data", function (data) {
responseString += data;
});
res.on("end", function () {
console.log("oldLogLevel: " + responseString);
});
});
var data = '\"critical\"';
req.write(data);
req.end();
}
setLogLevel();
I had to escape all the quotes and such, which was expected.
Now I'm trying to get CouchDb to accept a setting for compaction.
The problem is that I'm attempting to replicate this same request to a different setting but that setting doesn't have a simple structure, though it appears to be "just a String" as well.
The CouchDB API is yelling at me about invalid JSON formats and I've tried a boatload of escape sequences and attempts to parse the JSON in various ways to get it to behave the way I think it should.
I can use Chrome's Advanced Rest Client to send this payload, and it is successful:
Request Method: PUT
Request URL: http://localhost:5984/_node/couchdb#localhost/_config/compactions/_default
Request Body: "[{db_fragmentation, \"70%\"}, {view_fragmentation, \"60%\"}, {from, \"23:00\"}, {to, \"04:00\"}]"
This returns a "200 OK"
When I execute the following function in my node app, I get a response of:
{"error":"bad_request","reason":"invalid UTF-8 JSON"}
function setCompaction(){
configOptions.path = configOptions.path+'compactions/_default';
configOptions.method = 'PUT';
var responseString = '';
var req = http.request(configOptions, function(res){
res.on("data", function (data) {
responseString += data;
});
res.on("end", function () {
console.log("oldCompaction: " + responseString);
});
});
var data = "\"[{db_fragmentation, \"70%\"}, {view_fragmentation, \"60%\"}, {from, \"23:00\"}, {to, \"04:00\"}]\"";
req.write(data);
req.end();
}
Can someone point at what I'm missing here?
Thanks in advance.
You need to use node's JSON module to prepare the data for transport:
var data = '[{db_fragmentation, "70%"}, {view_fragmentation, "60%"}, {from, "23:00"}, {to, "04:00"}]';
// Show the formatted data for the requests' payload.
JSON.stringify(data);
> '"[{db_fragmentation, \\"70%\\"}, {view_fragmentation, \\"60%\\"}, {from, \\"23:
00\\"}, {to, \\"04:00\\"}]"'
// Format data for the payload.
req.write(JSON.stringify(data));
I want to call JSON data as much as the amount of data in the store. Here is the code:
storeASF.each(function(stores) {
var trano = stores.data['arf_no'];
Ext.Ajax.request({
results: 0,
url: '/default/home/getdataforeditasf/data2/'+trano+'/id/'+id,
method:'POST',
success: function(result, request){
var returnData = Ext.util.JSON.decode(result.responseText);
arraydata.push(returnData);
Ext.getCmp('save-list').enable();
Ext.getCmp('cancel-list').enable();
},
failure:function( action){
if(action.failureType == 'server'){
obj = Ext.util.JSON.decode(action.response.responseText);
Ext.Msg.alert('Error!', obj.errors.reason);
}else{
Ext.Msg.alert('Warning!', 'Server is unreachable : ' + action.response.responseText);
}
}
});
id++;
});
storeARF.loadData(arraydata);
StoreASF contains data[arf_no] which will be used as a parameter in Ajax request url. StoreASF could contain more than one set of the object store, so looping is possible. For every called JSON data from request would be put to array data, and after the looping is complete, I save it to storeARF with the loadData method.
The problem is, my data format is wrong since loadData can only read JSON type data. I already try JSON stringify and parse, but couldn't replicate the data format. Any suggestion how to do this? Thank you.
Rather than using Ext.util.Json.decode(), normalize the data in success() method using your own logic. For example:
success: function (response) {
console.log(response);
var myData = [];
Ext.Array.forEach(response.data, function (item) {
myData.push({
name: item.name,
email: item.email,
phone: item.phone
});
});
store.load();
}
I tried to get a random Wikipedia page over their API via Google Cloud Functions. The Wikipedia API works fine. This is my request:
https://de.wikipedia.org/w/api.php?action=query&format=json&generator=random
For testing you can change the format to jsonfm in see the result in the browser. Click here 👍.
But it seems that my functions get destroyed even before the request was completely successfully. If I want to parse the data (or even if I want to log that data) I got a
SyntaxError: Unexpected end of json
The log look like (for example) that (no I haven't cut it by myself):
DATA: ue||"},"query":{"pages":{"2855038":{"pageid":2855038,"ns":0,"title":"Thomas Fischer
Of course, that is not a valid json and can't be parsed. Whatever this is my function:
exports.randomWikiPage = function getRandomWikiPage (req, res) {
const httpsOptions = {
host: "de.wikipedia.org",
path: "/w/api.php?action=query&format=json&generator=random"
};
const https = require('https');
https.request(httpsOptions, function(httpsRes) {
console.log('STATUS: ' + httpsRes.statusCode)
console.log('HEADERS: ' + JSON.stringify(httpsRes.headers))
httpsRes.setEncoding('utf8')
httpsRes.on('data', function (data) {
console.log("DATA: " + data)
const wikiResponse = JSON.parse(data);
const title = wikiResponse.query.title
res.status(200).json({"title": title})
});
}).end();
};
I've already tried to return something here. Like that video explained. But as I look into the node docs https.request don't return a Promise. So return that is wrong. I've also tried to extract the on('data', callback) into it's own function so that I can return the callback. But I haven't a success with that either.
How have to look my function that it return my expected:
{"title": "A random Wikipedia Page title"}
?
I believe your json comes through as a stream in chunks. You're attempting to parse the first data chunk that comes back. Try something like:
https.request(httpsOptions, function(httpsRes) {
console.log('STATUS: ' + httpsRes.statusCode)
console.log('HEADERS: ' + JSON.stringify(httpsRes.headers))
httpsRes.setEncoding('utf8')
let wikiResponseData = '';
httpsRes.on('data', function (data) {
wikiResponseData += data;
});
httpRes.on('end', function() {
const wikiResponse = JSON.parse(wikiResponseData)
const title = wikiResponse.query.title
res.status(200).json({"title": title})
})
}).end();
};
I want to POST an Object via JSON to node.js server.
The Object structure is nested, and never succeeded to receive and parse correctly on node.js server site.
EDIT2
I found a solution: see the answer section...
EDIT
I found
console.log(body);
itself output
val1=hello&val2%5Bval3%5D=world
//= {"val1":"hello","val2[val3]":"world"}
weired JSON way
client.js
var data ={val1:"hello",val2:{val3:"world"}};
console.log(data); // -> *1
$.ajax({
url:"/",
type:"POST",
dataType: 'json',
data:data,
success:function (res)
{
resHandler(res);
}
});
*1 ChromeDevelopersTool
Object
val1: "hello"
val2: Object
val3: "world"
server.js
var onreq = function (req, res)
{
if(req.method == 'POST')
{
var body = '';
req.on('data', function (data)
{
body += data;
});
req.on('end', function ()
{
var json = qs.parse(body);
console.log(json.val1); //hello
console.log(json.val2); //undefined
console.log(json.val3); //undefined
console.log(JSON.stringify(json));
//{"val1":"hello","val2[val3]":"world"}
});
}
I understand
val2[val3]
is
val2.val3
However,
Problem 1
JSON.stringify prints out
{"val1":"hello","val2[val3]":"world"}
not
{val1:"hello",val2:{val3:"world"}}
It's ugly, and I don't know why it's like that.
Problem 2
I can never get {val3:"world"}
console.log(json.val3); //undefined
Anyone can explain, and how can I POST a nested JSON to node.js server?
Thanks.
Do NOT use JSON typed data on jQuery Ajax, instead use Stringified JSON
I created a WIKI
http://code.google.com/p/kenokabe/wiki/nestedJSONproblem
The nodejs server 'gets' this JSON stream from Twitter and sends it to the client:
stream.twitter.com/1/statuses/filter.json?track=gadget
The data returned to the client is 'chunked' JSON and both JSON.parse(chunk) and eval('(' + chunk + ')') on the client side result in parsing errors.
Concatenating the chucked pieces and waiting for the 'end' event isn't a solution either
I noticed previous samples used something like this on the client side that apparently worked before:
socket.onmessage = function(chunk) {
data = eval("(" + chunk.data + ")");
alert(data.user.screen_name);
I'm using this on the client side and it results in a parsing error:
var socket = new io.Socket();
socket.on('message', function(chunk) {
var data = eval('(' + chunk + ')'); // parsing error
alert(data.screen_name):
I know that its successfully returning a JSON chunk with:
var socket = new io.Socket();
socket.on('message', function(chunk) {
alert(chunk): // shows a JSON chunk
Server:
response.on('data', function (chunk) {
client.each(function(e) {
e.send(chunk);
});
Did something change or what else em I doing wrong?
UPDATE: The 'end' event does not fire because its streaming?
http.get({
headers: { 'content-type': 'application/json' },
host: 'stream.twitter.com',
path: '/1/statuses/filter.json?track...
}, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
client.each(function(e) {
e.send(chunk);
});
});
// does not fire
res.on('end', function () {
});
...
I'm looking into the difference with http 1.0 and http 1.1 as far as sending chunked data.
Look at the section titled Parsing Responses in Twitter's documentation.
Parsing JSON responses from the Streaming API is simple every object is returned on its own line, and ends with a carriage return. Newline characters (\n) may occur in object elements (the text element of a status object, for example), but carriage returns (\r) should not.
On the server side, keep accumulating chunks until you see the carriage return "\r". Once the carriage return is found, extract the string up to the carriage return, and that gives us one tweet.
var message = ""; // variable that collects chunks
var tweetSeparator = "\r";
res.on('data', function(chunk) {
message += chunk;
var tweetSeparatorIndex = message.indexOf(tweetSeparator);
var didFindTweet = tweetSeparatorIndex != -1;
if (didFindTweet) {
var tweet = message.slice(0, tweetSeparatorIndex);
clients.forEach(function(client) {
client.send(tweet);
});
message = message.slice(tweetSeparatorIndex + 1);
}
});
The client becomes simple. Simply parse the socket message as JSON in its entirety.
socket.on('message', function(data) {
var tweet = JSON.parse(data);
});
#Anurag I'cant add comments, however instead of
if (chunk.substr("-1") == "\r")
it should be:
if ( chunk.charCodeAt(chunk.length-2) == 13 )
The carriage return isn't the last character.
I would recommend piping the response into a JSON parser. You can use this: https://github.com/dominictarr/JSONStream