Home & blog  /  Tag: JSON  /

A look at native JSON support in ECMA5

posted: 18 May '12 21:48 tags: JSON, ECMA5, replacer, filter

A few weeks ago I started a series of posts on what we as JavaScript developers took delivery of with ECMA5. I recently gave a talk on the native support for JSON that arrived as part of ECMA5, and that's what I'll be looking at today.

Previously, of course, handling JSON data meant using something like jQuery or the JSON.org library. But JSON has grown hugely as a serious means of serialised data transfer that native support for it in JavaScript was very much necessary.

ECMA5 defines an object of the global scope, JSON. It comes with two rather predictable methods: parse() and stringify().

JSON.parse()

By far the simplest of the two is parse(), which, as its name suggests, parses a wel-formatted string of JSON data into a usable, traversable JavaScript object.

1var str = '{"foo": ["bar", "baz"]}',

2     obj = JSON.parse(str);

3console.log(obj.foo[1]); //baz

So far, so simple.

JSON.stringify()

Things get interesting with the second method, stringify(), however. This does the reverse operation: a JavaScript object is turned into a JSON string.

1var obj = {foo: ["bar", "baz"]},

2     str = JSON.stringify(obj);

3console.log(typeof str); //string

Remember JSON is derived from but not synonymous with JavaScript objects, and as such cannot accommodate certain data types. When stringifying your objects, note that any values that are not strings, numbers, booleans, null, arrays or objects, will be cast either to strings, nulls or, in the case of REGEXP objects, objects.

1var obj = [undefined, function(){}, new Date(), new RegExp()],

2     str = JSON.stringify(obj);

3console.log(str); //[null,null,"2012-05-18T21:39:58.333Z",{}]

A point worth making is that this method, perhaps more than any other, marks JavaSript's move away from its foundings as a rather limited, sober API.

For one, the advent of methods whose names have the "-ify" suffix is a clear sign of influence of third-party libraries. (Ten years ago, this method would have been called toString()).

Secondly, its optional second and third arguments are somewhat bizarre.

Replacer functions

The second argument to stringify() is a function or array which controls how and if a piece of data from the object makes it to the string.

As a function - called iteratively and recursively on each property - whatever you return, provided it's not undefined, gets through. (Interestingly, only undefined prevents this - no other falsy value.

1//allow only dogs through, no cats

2var obj = [

3         {name: "Fido", type: "dog"},

4         {name: "Flossy", type: "cat"}

5     ],

6     str = JSON.stringify(obj, function(key, val) {

7         if (!val.type || val.type == 'dog') return val;

8     });

So like with any filter or transformer, it's all about what you return. You might even want to tweak the value en-route.

As an array, it acts as a simple whitelist of properties names - not of values.

1//keep only animal names - not interested in type

2str = JSON.stringify(obj, ['name']);

3console.log(str);

There's a couple of things to note when using replacers. Firstly, remember the replacer is called recursively - and where your replacer is a function, this means starting with the object itself. I've known this catch people out who expect it to begin from within.

In other words, the first thing passed to the replacer is the object itself, so you will need to return a value for it.

1var obj = [

2         {name: "Fido", type: "dog"},

3         {name: "Flossy", type: "cat"}

4     ],

5     str = JSON.stringify(obj, function(key, val) {

6         if (val.type == 'dog') return val;

7     });

8console.log(str); //undefined

There we wrongly assume that the replacer starts work inside the object, i.e. on the first animal object. It doesn't; it starts on the array. Since our replacer allows data through only if it has a type property equal to "dog", our outer array clearly fails that test, so no value is passed through for it.

Recursive also means it will be called on any objects or arrays the replacer returns, whether it is merely passing them on from the source object or has itself created them.

Secondly, you can prevent properties from being transferred to the string only when dealing with objects, not arrays. If you return undefined from your replacer function and your source object is an array, the value will still go through, but arrive as null.

1//attempt to remove the cat

2var obj = ['dog', 'cat'],

3     str = JSON.stringify(obj, function(key, val) {

4         return val != 'cat' ? val : undefined;

5     });

6console.log(str); //["dog", null]

Thirdly and finally, replacers enact some curious data type coercion. Even though JSON is perfectly capable of handling numbers and booleans, if your replacer returns one of these the value will be cast into string form.

1var obj = ['something'],

2     str = JSON.stringify(obj, function() { return true; });

3console.log(str+' ('+typeof str+')'); //"true (string)"

Spacers

The third and final argument JSON.stringify() accepts allows you to indent the outputted string with a given padder.

You can pass either a string or a number. In the case of a string, that string is used as the padding. In the case of a number, that number of white spaces will be used for padding.

For some reason, the length of the padder is limited to ten, so if your string is longer it will be truncated, else if your number is over ten it will be limited to ten.

1var obj = {"foo": ["bar", "baz"]},

2     str = JSON.stringify(obj, null, 6);

3console.log(str);

That outputs in the following format:

1{

2     "foo":[

3             "bar",

4             "baz"

5     ]

6}

The interesting (but obviously necessary) thing here is that passing a spacer means the output is automatically broken into lines. (It would otherwise have been a single line.)

Et voila

While this is all great stuff, I have to confess I'm still a little shocked at the downright convenience of the whole thing. JavaScript developers just aren't used to that from the JS API!

The replacer argument in particular is downright luxurious, to the point of being of questionable necessity. If you really wanted to sanitise the object before turning it into a string (which in itself would surely not happen that often), you could easily do so yourself by making a copy and looping over it, changing it as required.

Don't get me wrong, I'm all for this brave new world of lavish, PHP-esq, rich API. I'm just taking a little while to acclimatise...

1 comments | post new

JSON and PHP: formatting and validating

posted: 04 Jan '12 21:05 tags: PHP, JSON, formatting

Formatting JSON to look all lovely

It's always a happy day when I manage to use JSON in PHP. As a JS developer, it sort of feels like I'm marrying the two technologies. Take that, serialised arrays - I'm using JSON.

PHP, of course, has support for encoding and decoding JSON via json_encode() and json_decode() respectively. What it can't do natively, though, is format JSON.

Why would you want to format JSON in PHP to make it look all nice and indented? Well, if the system you're building uses JSON for a config file, and you want users to be able to edit that config file within the system, in a textarea, say.

I then found this function, which largely does the job.

It can, however, get the indentation wrong sometimes. Also, it can leave whitespace at the end of lines. So I extended it slightly. The following three snippets should be inserted just before the final return statement.

Firstly, let's clear any whitespace left at the end of lines.

$result = preg_replace('/\t+\n/', "\n", $result);

Next, let's fix the indentation. What I noticed was that the script was indenting lines containing closing brackets/braces precisely double what it should be - so four tabs instead of two, for example. So, the following halves each case.

1function PR_callback($match) { return substr($match[1], 0, strlen($match[1]) / 2).$match[2]; }

2$result = preg_replace_callback('/^(\t+)(\]|\})/m', 'PR_callback', $result);

Note I'm using a callback on preg_replace() - this gives me greater control over the nature of my replacements. preg_replace() automatically forwards to my callback one argument - an array of the match. As ever, key 0 in the array contains the whole match and any subsequent keys contain any sub-matches my pattern looked for.

Lastly, either the script or (more probably) my hacks above end up killing some of the line breaks, so let's restore them.

$result = preg_replace('/([^\t]+)\t/', "$1\n\t", $result);

Et voila - nicely formatted JSON.

Make sure your JSON's valid

JSON and JavaScript Object Notation are not always the same thing. How so? Well, this is valid JS but INvalid JSON:

1var someObj = {

2     one: 'one',

3     two: 'two'

4}

...because the JSON spec demands that a) property names are quoted; b) property names and strings must be encased in double, not single quotes.

So if you end up with invalid JSON, how will you know? PHP >= 5.3 defines json_last_error(), which returns a flag saying what went wrong.

Confusingly, it returns something even if nothing went wrong - JSON_ERROR_NONE. It does not return false, so it is insufficient to check the validity of JSON with:

1$badJSON = '{one: "one"}';

2$array = json_decode($badJSON, true); //2nd param means we get back an assoc. array, not an object

3if (!json_last_error()) echo "All OK!";

Instead, the last line should be:

if (json_last_error() != JSON_ERROR_NONE) echo "All OK!";

PHP versions prior to 5.3

If you're running PHP prior to v.5.3, you won't have json_last_error(). Instead, you can simply check the truthy/falsy value of json_decode(), so:

1$array = json_decode($badJSON, true);

2if (!$array) echo "Problem!";

Obviously this approach is more crude and won't tell you what went wrong - just that something did.

If you're looking for somewhere to host your PHP, whether Linux or Windows hosting, it always pays to make use of a good web hosting review site to find the right web hosting provider. This is a particularly good one, especially their WordPress hosting search page if you’re still on your way finding a suitable home for your WordPress blog.

post a comment

posted: 02 Dec '10 18:55 tags: XML, JSON, remap, convert, data, AJAX, jQuery

I've had a great response to my XML-to-JSON convertor and remapper. Its ability to not only convert but also remap data on the fly, to your precise structural requirements, has gone down particularly well.

One flaw raised today, though, was that it didn't work with RSS fees. Standard XML feeds, yes, but not RSS. After some digging I realised why.

The plugin expected the iteration node (i.e. the repeated node whose data will form the sub-objects of your JSON data) to directly follow the root node, e.g.

1<root>

2     <news_story>

3         <headline>foo</headline>

4         <url>bar.html</url>

5     </news_story>

6</root>

But if your XML had more levels - such as the following, typical of RSS:

1<rss>

2     <channel>

3         <news_story>

4             <headline>foo</headline>

5             <url>bar.html</url>

6         </news_story>

7     </channel>

8</rss>

...it didn't play ball. This has been countered by the addition of a new second argument when calling the plugin, in which you pass a space-separated selector string matching the iteration node, so for the above two examples this would be 'root news_story' and 'rss channel news_story', respectively.

Happy days! to download, get usage info or view a demo.

I've also been asked about whether the plugin supports XML node attributes. No, but it will. Stay posted...

XML-to-JSON convertor and remapper

posted: 24 Nov '10 22:45 tags: XML, JSON, remap, convert, data, AJAX, jQuery

After much hair-pulling and cursing, my XML-to-JSON convertor and remapper is finally here.

The real power behind this plugin is its ability to not only convert but also REMAP your data on the fly. There are other plugins out there that handle conversion, but I've not seen any that also remap.

This has the potential to be hugely useful.

Imagine you're using a news ticker that requires JSON data (for the headlines, URLs etc), but you want to populate it with data from an XML feed you don't have control over, and whose property names and hierarchical structure is different from what your ticker needs:

1<item>

2     <one>foo</one>

3     <two>bar</two>

4     <three>

5         <four>foobar</four>

6     </three>

7</item>

but you need

1{

2     one: 'foo',

3     two: 'bar',

4     three: 'foobar'

5}

...so not only a conversion but also a structural change. You can achieve that with this plugin.

Head over here to download, get usage info or view a demo.

post a comment