JSON (JavaScript Object Notation) is very closely related to JavaScript objects because it's a subset of JavaScript. This task will demonstrate how to use the building blocks of JSON conversion: JSON.parse
and JSON.stringify.
Let's create the object that we'll later be converting to JSON.
module.exports = { ryan : { name: "Ryan Dahl", irc:'ryah', twitter:'ryah', github:'ry', location:'San Francisco, USA', description: "Creator of node.js" }, isaac : { name: "Isaac Schlueter", irc:'isaacs', twitter:'izs', github:'isaacs', location:'San Francisco, USA', description: "Author of npm, core contributor" }, bert : { name: "Bert Belder", irc:'piscisaureus', twitter:'piscisaureus', github:'piscisaureus', location:'Netherlands', description: "Windows support, overall contributor" }, tj : { name: "TJ Holowaychuk", irc:'tjholowaychuk', twitter:'tjholowaychuk', github:'visionmedia', location:'Victoria, BC, Canada', description: "Author of express, jade and other popular modules" }, felix : { name: "Felix Geisendorfer", irc:'felixge', twitter:'felixge', github:'felixge', location:'Berlin, Germany', description: "Author of formidable, active core developer" } };
This object contains profile information on some of the leading members of the Node Community (though it's entirely non-exhaustive and doesn't even contain all of the core development team). One thing to note here is the use of module.exports
. We'll be seeing more of this In Chapter 9, Writing Your Own Module. We're using module.exports
to modularize our profiles
object here in a bid to keep our code uncluttered. We can load any expression into module.exports
, save it as a separate file (which in our case, we'll call profiles.js)
, and use require
in our main file to dynamically load it at initialization.
var profiles = require('./profiles'), // note the .js suffix is optional
Nice and tidy. To convert our profiles
object into a JSON representation, we use JSON.stringify
, which will return a string composed of JSON data. We're going to fundamentally alter our object (which is now a string) using replace
.
profiles = JSON.stringify(profiles).replace(/name/g, 'fullname'),
Here we have called replace
, using a regular expression with the global g
option to change every occurrence of name
in our JSON string to fullname
.
But wait! There appears to be some kind of mistake. Felix's last name is missing an umlaut! Let's correct it by converting our JSON data back into an object, and correct his name by altering the value of the re-designated fullname
property:
profiles = JSON.parse(profiles); profiles.felix.fullname = "Felix Geisendörfer"; console.log(profiles.felix);
When we run our application, console.log
will output the following:
{ fullname: 'Felix Geisendörfer',
irc: 'felixge',
twitter: 'felixge',
github: 'felixge',
location: 'Berlin, Germany',
description: 'Author of formidable, active core developer' }
The first key is now fullname
, and Geisendörfer
is spelled correctly.
First, we have an everyday JavaScript object, which we serialize into a JSON representation. We also call the String.replace
method on our JSON string, changing every occurrence of name
into fullname
.
Using replace in this way and context isn't an advisable practice since any occurrences of name
are replaced. There could very easily have been other places in the string where name
may have existed, which would be replaced unintentionally. We used replace
here to affirm that profiles have become a JSON string, as we couldn't use replace
on an object.
Then we convert our modified JSON string back into an object, using JSON.parse
. To test that our keys were indeed transformed from name
to fullname
, and to affirm that we are again working with an object, we correct the felix
profile via profiles.felix.fullname
, and then log profiles.felix
to the console.
JSON is a highly versatile and flexible tool for cross-platform communication. Let's look at a more advanced application of the standard.
JSONP (JSON with Padding) is a cross-domain policy workaround that allows developers to interface with resources on other domains. It involves defining a callback function on the client side that handles JSON via its first parameter, then passing the name of this callback as a query argument in the src
attribute of a script
element, which points to a web service on another domain. The web service then returns the JSON data, wrapped in a function named according to the query argument set client side. It's possibly easier to illustrate this in code.
<html> <head> <script> var who = 'ryan'; function cb(o) { alert(o.name + ' : ' + o.description); } var s = document.createElement('script'), s.src = 'http://localhost:8080/?callback=cb&who=' + who; document.getElementsByTagName("head")[0].appendChild(s); </script> </head> </html>
We define a function called cb
which takes an object as its parameter, then we output the name
and description
properties. Prior to this, we set a variable called who
which will be passed to the server to grab specific data for us. We then dynamically inject a new script element, setting src
to a figurative third-party domain (which for easy demonstration is localhost) and adding callback
and who
query arguments. The value of callback
matches the name of our function cb
function. Our server uses this parameter to wrap JSON in a function invocation.
var http = require('http'), var url = require('url'), var profiles = require('./profiles'), http.createServer(function (request, response) { var urlObj = url.parse(request.url, true), cb = urlObj.query.callback, who = urlObj.query.who, profile; if (cb && who) { profile = cb + "(" + JSON.stringify(profiles[who]) + ")"; response.end(profile); } }).listen(8080);
We create a server, extract the callback
and who
query parameters, and write a response containing a function call passing our JSON data in as its parameter. This script is loaded by our client, where the cb
function is called and JSON is received into the function as an object (because it looks like one).
18.191.46.36