JSON.parse fails in function, works in console - javascript

I have a simple script that does a cross site request and gets data from a GitHub gist. The data from the Github API is returned as a JSON string. To allow further modification of the data, I want it as a JSON object.
// Create the XHR object.
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
var tmpJSON = "";
var gistData = "";
var gistID = "5789756";
var gitAPI = "https://api.github.com/gists/"
var gistQuery = gitAPI + gistID;
function incrementGist() {
gistData = createCORSRequest('GET', gistQuery);
gistData.send();
tmpJSON = JSON.parse(gistData.response);
}
In the html page, I have
<p><input type="button" value="Increment" OnClick="incrementGist()"></p>
If I actually hit the button, the error I get is:
Uncaught SyntaxError: Unexpected end of input
But if I subsequently open the console and run this:
var crap = JSON.parse(gistData.response);
it works just fine. This happens in both Firefox and Chrome. I really don't see why the JSON.parse command fails inside a function call, but not in the console. An actual page is set up here

The problem is that you're trying to read the response before the server answered.
You must read the response in a callback. For example :
gistData = createCORSRequest('GET', gistQuery);
gistData.onreadystatechange = function() {
if (gistData.readyState === 4) {
if (gistData.status === 200) {
tmpJSON = JSON.parse(gistData.response);
... use tmpJSON...
... which should not be called so as it is not JSON...
... maybe tmpObject ?
}
}
}
gistData.send();

That's because you are not waiting the request to actually finish. I don't know your API but try waiting the server response then parse your JSON. you could try with a SetTimeout first to see that it is working but you nee to do something like in jQuery with its' success:function(...) callback

Related

Unable to get JSON output from XMLHttpRequest

I've read a few StackOverflow posts, googled it but still can't get what I want.
I simply want to get a JSON from Google's API and import it to a variable so I can filter it the way I want, the following code is what I have so far:
<!DOCTYPE html>
<html>
<body>
Term: <input type="text" id="field1" value="Mc Donalds in New York"><br>
<button onclick="myFunction()">Search</button>
<script>
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
xhr.open(method, url, true);
xhr.responseType = 'json';
} else if (typeof XDomainRequest != "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
xhr.responseType = 'json';
} else {
xhr = null;
}
return xhr;
}
function myFunction() {
var termo = document.getElementById("field1").value;
var URL = "https://maps.googleapis.com/maps/api/place/textsearch/json?query="+termo.replace(" ","+")+"&key=HIDDEN_KEY";
var data = createCORSRequest('GET',URL);
if (!data) {
throw new Error('CORS not supported');
}
}
</script>
</body>
</html>
When I do:
console.log(data);
I get:
When I do:
JSON.parse(data.responseText);
I get:
Uncaught DOMException: Failed to read the 'responseText' property from
'XMLHttpRequest': The value is only accessible if the object's
'responseType' is '' or 'text' (was 'json').
What should I get on console.log:
https://pastebin.com/4H7MAMcM
How can I get the JSON from XMLHttpRequest correctly?
Also worth mentioning, I'm using Cross-Origin Resource Sharing (CORS) because I couldn't access the domain from my local IP.
--Edit--
Phil thought this was a matter of not being able to return response from a asynchronous, but its wrong, I've tried using Ajax, XMLHttpRequest and now using CORS, the duplicate notation was incorrect, please remove it.
This behaviour is documented on MDN;
If responseType is set to anything other than the empty string or "text", accessing responseText will throw InvalidStateError exception.
Instead, you need to use the response property. Since you specified json as the responseType, response will be a JavaScript object (no need to JSON.parse it).
Aside from this, you'll also need to treat the AJAX request as asynchronous, rather than synchronous. For more info on that, see How do I return the response from an asynchronous call?.
Eventually, you should end up with something like;
function createCORSRequest(method, url, cb) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
xhr.open(method, url, true);
xhr.responseType = 'json';
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
cb(this.response);
}
}
xhr.send(null);
} else if (typeof XDomainRequest != "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
xhr.responseType = 'json';
} else {
xhr = null;
}
return xhr;
}
createCORSRequest('POST', '/echo/json/', function (response) {
console.log(response);
});
https://jsfiddle.net/ez3xt6ys/
However, the browser support seems patchy for this at best; https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/response. Instead, it is more common to see the responseType being left as text, and for people to JSON.parse() the responseText property.

Javascript Google Geocode Returning Undefined (Can't figure callback function)

I'm trying to store the values of the latitude and the longitude from the response that I get from the Google Geocoding API. Right now I am using a fixed location in California. My HTTP request keeps returning undefined.
I've been looking around a lot and I see that people have this same problem, but I can't wrap my head around making a callback function which seems to be the solution, could anybody explain to me how to do this? I know that my CORS request is working because I am using it to talk GET from a Heroku server that I set up.
var geocode = new XMLHttpRequest();
geocode = createCORSRequest('GET', 'https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=');
geocode.onload = function() {
var geoData = JSON.parse(geocode.responseText); // parse the JSON from geocode response
var results = geoData["results"]; // create variable for results
var userLong = results["geometry"]["location"]["lng"]; // parse the latitude
var userLat = results["geometry"]["location"]["lat"]; // parse the longitude
console.log(userLong);
console.log(userLat);
}
createCORSRequest()
// Create the XHR object.
function createCORSRequest(method, url) {
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, false);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
Look like you're following HTML5Rocks site Using CORS article.
Once you create the xhr request. You must invoke send() method to trigger the ajax call.
var geocode = new XMLHttpRequest();
geocode = createCORSRequest('GET', 'https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=');
geocode.onload = function() {
var geoData = JSON.parse(geocode.responseText); // parse the JSON from geocode response
var results = geoData["results"]; // create variable for results
var userLong = results["geometry"]["location"]["lng"]; // parse the latitude
var userLat = results["geometry"]["location"]["lat"]; // parse the longitude
console.log(userLong);
console.log(userLat);
}
geocode.send();
Unless you call geocode.send(), the value will be undefined as no request has been fired.
Assuming createCORSRequest returns an xhr or xhr-like object (which seems to be the typical boilerplate for createCORSRequest from places like the HTML5 Rocks website), you need to include geocode.send(); at the end of your code. Otherwise the request never fires and therefore the onload handler never gets called.

CORS request firing without sending form data on window.onbeforeunload event in IE9

I've got a pretty simple function which is designed to grab the form data and send it via a CORS request. Basically it looks like this...
window.onbeforeunload = function() {
formData = getFormData();
logAbandonment(formData);
// return formData;
// alert(formData);
}
function logAbandonment(formData)
{
if(!cors_request) {
cors_request = true;
} else {
return;
}
var url = 'http://mydomain.lan/sub/index.php';
var xhr = createCORSRequest('POST', url);
if (!xhr) {
console.log('Error: CORS not supported.');
}
xhr.send(formData);
}
function createCORSRequest(method, url)
{
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
xhr.onprogress = function () { };
xhr.ontimeout = function () { };
xhr.onerror = function () { };
xhr.onload = function() { };
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
}
function getFormData()
{
if(typeof FormData == 'undefined') {
return serialize(document.getElementById('AppForm'));
} else {
return new FormData(document.getElementById('AppForm'));
}
}
Because this is IE9 I am working with, I am using the XDomainRequest javascript object.
It is successfully firing the ajax request, but here is where I am having a problem. It is firing it without sending the formData unless I uncomment either of the return or alert lines, in which case it works perfectly. When I do that, I can see the correct data it is supposed to be saying in the alert.
Another thing I noticed is this only happens when I either close the browser or close the tab. If I refresh the page, it works exactly like I want it to.
I thought maybe IE9 had some weird method of destroying the dom before the request was finished going out, but unfortunately, I can't figure out a way to set this to async false on XDomainRequest.
I've also tried setting a timeout, but that seems to break it completely.
Not an answer as much as a work-around, but I found this works perfectly when appending the query string onto the end of the url when calling xdr's open method.

Setting server response data to a variable to work with

Hey guys I am using a executePostHttpRequest function that looks exactly like the code posted below. Currently when I run the function I get a server response with the appropriate data but I am not sure how I can work with the response data? how do I store it in to a variable to work with?
Javascript executePostHttpRequest
function executePostHttpRequest(url, toSend, async) {
console.log("====== POST request content ======");
console.log(toSend);
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
xmlhttp.open("POST", url, async);
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.setRequestHeader("Content-length", toSend.length);
xmlhttp.send(toSend);
console.log("====== Sent POST request ======");
}
Here is what I am doing to execute it. Using Javascript
var searchCriteria = JSON.stringify({
displayName : search_term
});
console.log("Search: "+searchCriteria) //Search: {"name":"John, Doe"}
var response = executePostHttpRequest("/web/search", searchCriteria, true);
console.log(response) //undefined
So currently the console.log for response shows undefined. But if I take a look at the network tab on Chrome Dev Tools and look at the /web/search call I see a JSON string that came back that looks something like this.
[{"id":"1","email":"john.doe#dm.com","name":"John, Doe"}]
I'd like to be able to display the data from this response to a HTML page by doing something like this.
$("#id").html(response.id);
$("#name").html(response.name);
$("#email").html(response.email);
I tried taking another route and using Jquery POST instead by doing something like this.
var searchCriteria = JSON.stringify({
displayName : search_term
});
console.log("Search: "+searchCriteria) //Search: {"name":"John, Doe"}
$.post("/web/search", {
sendValue : searchCriteria
}, function(data) {
$.each(data, function(i, d) {
console.log(d.name);
});
}, 'json').error(function() {
alert("There was an error searching users! Please contact administrator.");
});
But for some reason when this runs I get the "There was an error" with no response from the server.
Could someone assist me with this? Thank you for taking your time to read it.
Your executePostHttpRequest function doesn't do anything with the data it's receiving. You would have to add an event listener to the XMLHttpRequest to get it:
function getPostData(url, toSend, async, method) {
// Create new request
var xhr = new XMLHttpRequest()
// Set parameters
xhr.open('POST', url, async)
// Add event listener
xhr.onreadystatechange = function () {
// Check if finished
if (xhr.readyState == 4 && xhr.status == 200) {
// Do something with data
method(xhr.responseText);
}
}
}
I've added the method parameter for you to add a function as parameter.
Here's an example of what you were trying to do:
function displayStuff(jsonString) {
// Parse JSON string
var data = JSON.parse(jsonString)
// Loop over data
for (var i = 0; i < data.length; i++) {
// Get element
var element = data[i]
// Do something with its attributes
console.log(element.id)
console.log(element.name)
}
}
getPostData('/web/search', searchCriteria, true, displayStuff)

Javascript "Invalid calling object" error with xml

I would like to display a list when a user is typping text (like autocompletion).
I load a xml with the list and when the user is typping text, a javascript function loops into the xml to find matches.
Everything is ok except on Internet Explorer where it SOMETIMES displays this error : "SCRIPT65535: Invalid calling object".
The first time i call the js function to loop into the xml always works but if i wait 5 seconds before calling it again, it will dispay the error.
If i wait less than 1 second it won't display the error.
It may be because in the loop i call the getAttribute() method... when i remove it there is no error.
Thx for any help !
Here is the code :
Ajax loading :
var ajax = {};
ajax.getXMLHttpRequest = function(){
var xhr = null;
if(window.XMLHttpRequest || window.ActiveXObject){
if(window.ActiveXObject){
try{
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e){
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
}
else xhr = new XMLHttpRequest();
}
else return null;
return xhr;
};
ajax.loadFile = function(callback){
var xhr = ajax.getXMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)){
callback(xhr.responseXML);
xhr = null;
}
};
xhr.open("GET", 'file.xml', true);
xhr.setRequestHeader("Content-Type", "text/xml");
xhr.send(null);
};
ajax.loadFile(callback);
Callback function :
var xml_nodes = '';
function callback(response){
xml_nodes = response.getElementsByTagName('node');
}
Then a mouseclick or whatever triggers this function :
function buttonClick(){
for(var i=0; i<xml_nodes.length; i++){
var attr = xml_nodes[i].getAttribute('attr');
}
}
This is a caching problem that only occurs in Internet Explorer. Your callback(response) function assigns the node elements to the xml_nodes variable. These nodes are a part of the response which is a part of the XMLHttpRequest, which gets disposed because you have no pointers to it.
The buttonClick function will iterate over the xml_nodes that are connected to disposed XMLHttpRequest's. And these are disposed because you have no pointers to it, and are therefore invalid objects.
A simple workaround will be caching your requests in an array. However this will result in large amounts of unwanted memory usage. You should create objects from the xml response and store them. These new objects won't have any pointers to the responseXML and are therefore valid objects.
Hope this helped, had the same problem to :)

Categories