Parsing JSON from XmlHttpRequest.responseJSON
Asked Answered
L

5

132

I'm trying to parse a bit.ly JSON response in javascript.

I get the JSON via XmlHttpRequest.

var req = new XMLHttpRequest;  
req.overrideMimeType("application/json");  
req.open('GET', BITLY_CREATE_API + encodeURIComponent(url)
          + BITLY_API_LOGIN, true);  
var target = this;  
req.onload  = function() {target.parseJSON(req, url)};  
req.send(null);

parseJSON: function(req, url) {  
if (req.status == 200) {  
    var jsonResponse = req.responseJSON;  
    var bitlyUrl = jsonResponse.results[url].shortUrl;  
}

I do this in a firefox addon. When I run I get the error "jsonResponse is undefined" for the line var bitlyUrl = jsonResponse.results[url].shortUrl;. Am I doing anything wrong in parsing JSON here? Or what is wrong with this code?

Loring answered 29/12, 2009 at 5:59 Comment(0)
L
287

New ways I: fetch

TL;DR I'd recommend this way as long as you don't have to send synchronous requests or support old browsers.

A long as your request is asynchronous you can use the Fetch API to send HTTP requests. The fetch API works with promises, which is a nice way to handle asynchronous workflows in JavaScript. With this approach you use fetch() to send a request and ResponseBody.json() to parse the response:

fetch(url)
  .then(function(response) {
    return response.json();
  })
  .then(function(jsonResponse) {
    // do something with jsonResponse
  });

You can use the await statement instead of the promise notation, to keep the code readable:

const response = await fetch(url);
const jsonResponse = await response.json();
// do something with jsonResponse

Compatibility: The Fetch API is not supported by IE11 as well as Edge 12 & 13. However, there are polyfills.

New ways II: responseType

As Londeren has written in his answer, newer browsers allow you to use the responseType property to define the expected format of the response. The parsed response data can then be accessed via the response property:

var req = new XMLHttpRequest();
req.responseType = 'json';
req.open('GET', url, true);
req.onload  = function() {
   var jsonResponse = req.response;
   // do something with jsonResponse
};
req.send(null);

Compatibility: responseType = 'json' is not supported by IE11.

The classic way

The standard XMLHttpRequest has no responseJSON property, just responseText and responseXML. As long as bitly really responds with some JSON to your request, responseText should contain the JSON code as text, so all you've got to do is to parse it with JSON.parse():

var req = new XMLHttpRequest();
req.overrideMimeType("application/json");
req.open('GET', url, true);
req.onload  = function() {
   var jsonResponse = JSON.parse(req.responseText);
   // do something with jsonResponse
};
req.send(null);

Compatibility: This approach should work with any browser that supports XMLHttpRequest and JSON.

JSONHttpRequest

Disclosure: I'm the owner of Pixels|Bytes. I thought that my script was a good solution for the original question, but it is rather outdated today. I do not recommend to use it anymore.

If you prefer to use responseJSON, but want a more lightweight solution than JQuery, you might want to check out my JSONHttpRequest. It works exactly like a normal XMLHttpRequest, but also provides the responseJSON property. All you have to change in your code would be the first line:

var req = new JSONHttpRequest();

JSONHttpRequest also provides functionality to easily send JavaScript objects as JSON. More details and the code can be found here: http://pixelsvsbytes.com/2011/12/teach-your-xmlhttprequest-some-json/.

Loner answered 7/12, 2011 at 14:30 Comment(6)
+1; IMO this was the real answer to the question - no jQuery just plain old vanilla XMLHttpRequest; just what the question was about.Bamby
Theres a jquery version too. If you are getting crossbrowser issues try it, usually framework`s handle these problems better: api.jquery.com/jquery.parsejsonMcginty
Four years later and this is still helping people. :) Linking to the blog is fine IMO, since it really is the full answer to the question with sample code and a download. Thank you!Chilton
"Use a new library" is not as helpful as one might think.Gaston
@GrunionShaftoe Could you please explain, what do mean? I'm not suggesting to use a new library. My recommended solution fetch is standard JavaScript.Loner
I find the fetch(url).then(...).then(...) notation quite cumbersome. An alternative is to create an async function and use await. A one-liner to answer the original question is async () => { responseJson = (await fetch(url)).json() /* do something with responseJson */ }. Alternatively, you can do something like response = await fetch(url) if you need the full response to check for example response.ok and the json can be extracted with response.json(). The caveat to async functions is that you are required to return a Promise if you have a return value.Heller
D
37

You can simply set xhr.responseType = 'json';

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1');
xhr.responseType = 'json';
xhr.onload = function(e) {
  if (this.status == 200) {
    console.log('response', this.response); // JSON response  
  }
};
xhr.send();
  

Documentation for responseType

Duclos answered 4/8, 2017 at 8:28 Comment(2)
There's a major caveat here: neither IE, nor current versions of Edge support this (maybe Edge will finally after transitioning to Chromium)Planometer
Edge support added in v79Wiesbaden
G
3

Use nsIJSON if this is for a FF extension:

var req = new XMLHttpRequest;
req.overrideMimeType("application/json");
req.open('GET', BITLY_CREATE_API + encodeURIComponent(url) + BITLY_API_LOGIN, true);
var target = this;
req.onload = function() {target.parseJSON(req, url)};
req.send(null);

parseJSON: function(req, url) {
if (req.status == 200) {
  var jsonResponse = Components.classes["@mozilla.org/dom/json;1"]
      .createInstance(Components.interfaces.nsIJSON.decode(req.responseText);
  var bitlyUrl = jsonResponse.results[url].shortUrl;
}

For a webpage, just use JSON.parse instead of Components.classes["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON.decode

Glochidium answered 21/3, 2011 at 6:29 Comment(0)
F
3

Note: I've only tested this in Chrome.

it adds a prototype function to the XMLHttpRequest .. XHR2,

in XHR 1 you probably just need to replace this.response with this.responseText

Object.defineProperty(XMLHttpRequest.prototype,'responseJSON',{value:function(){
 return JSON.parse(this.response);
},writable:false,enumerable:false});

to return the json in xhr2

xhr.onload=function(){
 console.log(this.responseJSON());
}

EDIT

If you plan to use XHR with arraybuffer or other response types then you have to check if the response is a string.

in any case you have to add more checks e.g. if it's not able to parse the json.

Object.defineProperty(XMLHttpRequest.prototype,'responseJSON',{value:function(){
 return (typeof this.response==='string'?JSON.parse(this.response):this.response);
},writable:false,enumerable:false});
Firman answered 12/7, 2013 at 7:27 Comment(1)
I would define a getter instead of a function attribute if I were you. Just replace value with get in the object passed to Object.defineProperty, and you can use responseJSON like you would any other response variable.Ingate
B
2

I think you have to include jQuery to use responseJSON.

Without jQuery, you could try with responseText and try like eval("("+req.responseText+")");

UPDATE:Please read the comment regarding eval, you can test with eval, but don't use it in working extension.

OR

use json_parse : it does not use eval

Buckeye answered 29/12, 2009 at 6:19 Comment(1)
For a Firefox addon running with chrome privs, don't try to eval anything you get from an outside source. Instead, use JSON.parse (at least in FF 3.5 and later).Vitrics

© 2022 - 2024 — McMap. All rights reserved.