Skip to content

Instantly share code, notes, and snippets.

@tsaniel
Forked from 140bytes/LICENSE.txt
Created July 17, 2011 07:37
Show Gist options
  • Save tsaniel/1087317 to your computer and use it in GitHub Desktop.
Save tsaniel/1087317 to your computer and use it in GitHub Desktop.
JSON stringify

JSON stringify

A simple JSON stringify function which requires Array.prototype.map method.

Contributors

The following users provided lots of ideas and improvements of the code.

@Kambfhase, @hartrock, @atk, @haochi, @jed

Actually, I just provided a deficient version at first. The other versions were created by these users(especially @atk). You may see the discussion below.

function f(
a, // input (expect object)
b, // placeholder
c // placeholder
){
for (b in
// if the input is an object, assign an empty array to c and loop through the properties of it;
// otherwise assign false to c and iterate over nothing.
// but since it's just a roughly checking, a string "[object Object]" will falsely be identified as object,
// and starts the iteration in most of the browsers -
// as properties on strings can be accessed like arrays(except lower IE version, e.g. IE7).
// (reference: http://kangax.github.com/es5-compat-table/#property-access-on-strings).
// anyway, the function still works although there may be unwanted loopings with a bit slower.
(c = a == '' + {} && []) && a
)
// fill array with object-style key: value pairs, call ourself recursively to escape keys and values.
c.push(f(b) + ':' + f(a[b]));
// check if the input is a string,
return '' + a === a ?
// output as a string literal;
'"' + a + '"' :
// otherwise check if the input is present and an array (has .map),
a && a.map ?
// output as an array literal;
'[' + a.map(f) + ']' :
// otherwise check if the input is an object (c is an array rather than false),
c ?
// output as an object literal;
'{' + c + '}' :
// otherwise use plain coercion (mostly for number/boolean/null literals).
a
}
function f(a,b,c){for(b in(c=a==""+{}&&[])&&a)c.push(f(b)+":"+f(a[b]));return""+a===a?'"'+a+'"':a&&a.map?"["+a.map(f)+"]":c?"{"+c+"}":a}
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2011 YOUR_NAME_HERE <YOUR_URL_HERE>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "JSON stringify",
"description": "A JSON stringify function",
"keywords": [
"JSON",
"stringify"
]
}
<!DOCTYPE html>
<title>JSON stringify</title>
<div>Expected value: <b>{"a":123,"b":true,"c":false,"d":null,"e":{"a":"some text"},"f":[1,2,3],"g":["eins","zwei","drei"],"h":[{"one":1},{"one":1,"two":2}],"i":[{"one":1},{"one":1,"two":[{"three":3},[1,2,3]]}]}</b></div>
<div>Actual value: <b id="ret"></b></div>
<script>
var myFunction = function f(a,b,c){for(b in(c=a==""+{}&&[])&&a)c.push(f(b)+":"+f(a[b]));return""+a===a?'"'+a+'"':a&&a.map?"["+a.map(f)+"]":c?"{"+c+"}":a};
document.getElementById( "ret" ).innerHTML = myFunction({
a: 123,
b: true,
c: false,
d: null,
e: {a: "some text"},
f: [1, 2, 3],
g: ["eins", "zwei", "drei"],
h: [{one:1}, {"one":1, two:2}],
i: [{one:1}, { "one":1, two:[ {three:3}, [1,2,3] ] } ]
})
</script>
@atk
Copy link

atk commented Jul 26, 2011

nice idea! Yes, that would probably work:

function f(a,b,c){for(b in(a==''+{}&&[])&&a)c.push(f(b)+':'+f(a[b]));return''+a===a?'"'+a+'"':a&&a.map?'['+a.map(f)+']':c?'{'+c+'}':a}

@tsaniel
Copy link
Author

tsaniel commented Jul 26, 2011

@jed Thanks, the tip is great! I think it should be written in the Byte-Saving Techniques.
@atk Thanks as well. But I think the code is missing something...

@jed
Copy link

jed commented Jul 26, 2011

@atk are you sure about that operator precedence?

@atk
Copy link

atk commented Jul 26, 2011

@jed + > == > &&, so this should work, yes. I have to admit I printed out the sheet of the precedence order from mozilla documentation.

@michaelficarra
Copy link

So.... nobody seems to have mentioned the failure case with strings containing ".

@atk
Copy link

atk commented Jul 27, 2011

It was obvious to us that the contents of strings are not correctly escaped, so we didn't mention it. We're still in the process of golfing for more space for the necessary filter.

@tkissing
Copy link

'' + a === a could be shortened to a && a.sub (or am I missing something?)

@Kambfhase
Copy link

that will disable support for non-browser environments like (Node-js) Rhino

@tkissing
Copy link

Ah, right. Bummer. And .trim won't work in older JS implementations. Too bad...

@tsaniel
Copy link
Author

tsaniel commented Nov 11, 2011

And so is Array#map...

@atk
Copy link

atk commented Nov 11, 2011

But Array#map will work in node.js - and for older implementations we already have a working polyfill.

@tsaniel
Copy link
Author

tsaniel commented Nov 11, 2011

In that way, we have polyfill for trim as well.

@adius
Copy link

adius commented Mar 7, 2012

You should use a.pop instead of a.map as in DOMinate. Much better browser compatibility!

@atk
Copy link

atk commented Mar 9, 2012

@adius: doesn't help, because we'll need a.map anyway. A working version (alas, with 214bytes too long) is here:

function s(a,b,c){for(b in(c=a==''+{}&&[])&&a)c.push(s(b)+':'+s(a[b]));return''+a===a?'"'+a.replace(/[\x00-\x32\\]/g,function(x){return'\x'+x.charCodeAt().toString(16)})+'"':a&&a.map?'['+a.map(s)+']':c?'{'+c+'}':a}

I've not yet tested if it still works if you replace function(x){return'\x'+x.charCodeAt().toString(16)} with function(x){return escape(x).replace(/%/g,'\\x')}, saving 2 further bytes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment