0

So, I've been writing a webpage and for part of this project I needed to access a JSON file in the JavaScript code. So I wrote the following:

function xhrSuccess () { this.callback.apply(this, this.arguments); }
function xhrError () { console.error(this.statusTest); }

function loadFile(sUR) {
    var oReq = new XMLHttpRequest();
    oReq.callback = readJSON;
    oReq.onload = xhrSuccess;
    oReq.onerror = xhrError;
    oReq.open("GET", sURL, true);
    oReq.send(null);
    return oReq.callback();
}

function readJSON() {
    console.log(this.responseText);
    var my_JSON_object = JSON.parse(this.responseText);
    return my_JSON_object;
}

And it's called in this way:

var data = loadFile("http://localhost:8000/json/data.json");

But I keep getting a few errors, and I'm not sure as to their meaning or even why I'm getting them.

Uncaught SyntaxError: Unexpected end of input
Uncaught SyntaxError: Unexpected token :

The first error occurs when I try to parse my JSON.

I have no idea what this means at all, because it normally relates to some sort of misplaced semi-colon or related object that causes a literal syntax error but the error was caught at the JSON.parse() line so I'm not sure what I'm doing wrong.

Anyway, any help would be welcome.

And here's my JSON:

{ "data": [
     {"file": "temp", "title": "This is a test", "date": "August 21, 2015"},
     {"file": "temp2", "title": "This is also a test", "date": "August 20, 2015"},
    {"file": "temp3", "title": "This is the final test", "date": "August 22, 2015"}
] }
Woody1193
  • 7,252
  • 5
  • 40
  • 90
  • 2
    It means the data within `data.json` is not valid JSON in some way. Paste it into http://jsonlint.com/ to find out what's wrong – CodingIntrigue Aug 21 '15 at 11:03
  • 1
    In order to generate valid JSON manually (if this is what you're doing), I usually build a standard js Object and then output the stringified result to console - then I copy and paste that JSON, to be sure it's valid. – jonny Aug 21 '15 at 11:12
  • 1
    check this link : http://stackoverflow.com/questions/1973140/parsing-json-from-xmlhttprequest-responsejson This may solve your problem. – bob Aug 21 '15 at 11:15
  • By default, if there is no `return` statement in a function, then the function returns `undefined`. Here, `loadFile` doesn't contain any return statement, so it returns undefined: that's why your `data` variable is undefined. The data is only accessible in the `readJSON` function ;) – Benoit Aug 21 '15 at 11:46
  • @RGraham Thanks for the comment. I checked and jsonlint.com said it was valid. – Woody1193 Aug 22 '15 at 01:50
  • @pawanbhardwaj But I'm referencing `responseText` not `responseJSON` so that shouldn't be a problem – Woody1193 Aug 22 '15 at 01:51
  • @Brunt In that case, what should `loadFile` return? – Woody1193 Aug 22 '15 at 01:52

2 Answers2

3

If you try to parse a JSON object that is not stringified, you'll get Unexpected token errors. Are you sure the JSON content you get is stringified?

Next, you return oReq.callback(); in your loadFile function. When calling loadFile later on (var data = loadFile("http://localhost:8000/json/data.json");), the line return oReq.callback(); is immediately executed so the value of this.responseText is empty string ("") by the time readJSON is executed, that's why you should get an error like "Unexpected end of input" (because JSON.parse('') raises that error).

One way of doing it is to process your data directly in the readJSON function because that's where your data (after the asynchronous request is resolved) will be. But this can lead to poor code quality if you have to make one/several requests later on with the data of your first request:

var xhr1 = new XMLHttpRequest();
xhr1.onload = function (req) {
  var data = JSON.parse(req.responseText);
  var postId = data.posts[0].id;
  var xhr2 = new XMLHttpRequest();
  xhr2.onload = function (req2) {
    // imagine if we sill have 2 more requests before getting the data needed...
  };
  xh2.open('GET', 'http://example.com/posts/' + postId, true);
  xh2.send();
};
xhr1.open('GET', 'http://example.com/users/1', true);
xhr1.send();

This callback architecture is called "the Pyramid of Doom". Nowadays, we have tools that let us write better code for these use cases, for example Promises. It's currently in draft (part of ES6/ES2015 specification), not all browsers support it, but you can use polyfills or libraries/frameworks that implement it (e.g. $.ajax from jQuery, $q from AngularJS, Promise from babel-polyfill...).

In your case, we can use the babel-polyfill from Babel. First, you need to include the polyfills before your Javascript code, for example using the cdnjs.com link:

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.15/browser-polyfill.min.js"></script>

Then, your code will look like this:

function loadData(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();

    xhr.onload = function onload(request) {
      if (request.status >= 200 && request.status < 400) {
        var data = JSON.parse(request.responseText);
        resolve(data);
      } else {
        // We reached our target server, but it returned an error
        reject(Error('Could not load any data'));
      }
    };
    xhr.onerror = function onerror(err) {
      reject(err);
    };

    xhr.open("GET", url, true);
    xhr.send();
  });
}

loadData('http://example.com')
  .then(function (data) {
    console.log('Got the data!', data);
    // do something with the data..., for example, if data is a stringified json:
    return JSON.parse(data);
  }, function (err) {
    console.log('There was an error', err);
  })
  .then(function (parsedJson) {
    console.log('Parsed JSON', parsedJson);
  });

Link to the plunker.

There are tons of guides/tutorials about asynchronous requests and promises on the web, I suggest you reading about it! ;)

Benoit
  • 751
  • 5
  • 8
  • Ok thanks. This fixes the errors, but it does lead to a new problem but I don't think this is with my code and it's off-topic. – Woody1193 Aug 24 '15 at 02:15
1

The uncaught SyntaxError means that the JSON you are about to parse it not properly formatted.

Where are you setting this.responseText? I think you should instead use sMsg.responseText. Perhaps adding more surrounding code to your question can clarify this.

Netzdoktor
  • 526
  • 4
  • 15
  • I added the JSON file I'm using but there's really nothing else to this. When I put the JSON into a string and call the parse from another function it works fine but when I try to pull it from the .json file there's problems. However, the JSON itself is logged to the console just fine so I don't see the problem. – Woody1193 Aug 22 '15 at 02:20
  • Are you using a NodeJS server or something similar to deliver the JSON? Have you checked in your browsers console, that `http://localhost:8000/json/data.json` is in fact the content of `data.json`? Perhaps you have an issue with file type (should be `application/json`) or line feeds (should be the correct one for your OS). – Netzdoktor Aug 22 '15 at 08:28
  • Yes, `http://localhost:8000/json/data.json` actually navigates to the file itself; I checked in my browser. As for the server, I set up one using a python command. Not sure what you mean by file type or line feed issues. Could you be more specific? – Woody1193 Aug 22 '15 at 12:26