Skip to content

Instantly share code, notes, and snippets.

@andrew8088
Last active August 23, 2022 07:54
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save andrew8088/6f53af9579266d5c62c8 to your computer and use it in GitHub Desktop.
Save andrew8088/6f53af9579266d5c62c8 to your computer and use it in GitHub Desktop.
A simple implementation of JSON.stringify; covers every case I could think of
function stringify(obj) {
if (typeof obj !== 'object' || obj === null || obj instanceof Array) {
return value(obj);
}
return '{' + Object.keys(obj).map(function (k) {
return (typeof obj[k] === 'function') ? null : '"' + k + '":' + value(obj[k]);
}).filter(function (i) { return i; }) + '}';
}
function value(val) {
switch(typeof val) {
case 'string':
return '"' + val.replace(/\\/g, '\\\\').replace('"', '\\"') + '"';
case 'number':
case 'boolean':
return '' + val;
case 'function':
return 'null';
case 'object':
if (val instanceof Date) return '"' + val.toISOString() + '"';
if (val instanceof Array) return '[' + val.map(value).join(',') + ']';
if (val === null) return 'null';
return stringify(val);
}
}
var assert = require('assert');
describe('stringify', function () {
function check(o) {
return function () {
assert.equal(stringify(o), JSON.stringify(o));
};
}
it("string", check('andrew'));
it('string with special chars', check('this"is a \\test'));
it("number", check(10));
it("true", check(true));
it("false", check(false));
it("null", check(null));
it("array", check(['one', 'two', 1, { name: 'andrew'}]));
it("empty object", check({}));
it("string prop", check({ name: "andrew" }));
it("number prop", check({ name: "andrew", age: 24 }));
it("boolean prop", check({ name: "andrew", age: 24, married: false, single: true }));
it("date prop", check({ name: "andrew", age: 24, married: false, single: true, date: new Date() }));
it("array prop of strings", check({ array: ['one', 'two'] }));
it("array prop of differing values", check({ array: ['one', 2, false, null, { value: 'five', or: 2}] }));
it("null prop", check({ array: ['one', 'two'], nothing: null }));
it("object prop", check({ name: 'andrew', address: { streetAddress: '21st street', city: 'New York', state: 'NY'}}));
it("functions", check({ name: 'andrew', doSomething: function () {} }));
it("functions in array property", check({ name: 'andrew', doSomething: [function () {}] }));
});
Copy link

ghost commented Oct 20, 2018

In line 2 obj === null will return false if obj is null.
Is that expected behavior?

@PoojaSSW
Copy link

PoojaSSW commented Aug 1, 2019

@ghost: Why would the condition return to false, when obj is null, I dont understand. I think its a valid condition. Please explain if I missed something in understanding the code

@roblevintennis
Copy link

roblevintennis commented Sep 23, 2019

@PoojaSSW @ghost I think what's happening there is if null is passed in, it'll get into value which will do typeof null which actually returns object and then within that case we ultimately return the string 'null', which is the same result as you'd get if you called the native JSON.stringify(null). Note also, that the check test method is actually comparing this custom stringify to the native one for all cases (with the obvious assumption that if the results are equal, the custom one is correct). Another approach I've seen, is to wrap the custom stringify in JSON.parse and ensure it's deserialized back to the original raw JavaScript which also seems like a good approach.

In line 2 obj === null will return false if obj is null.

I don't see how since if you do null === null console is saying true

@roblevintennis
Copy link

roblevintennis commented Sep 23, 2019

It looks like this will fail for arrays with undefined values and will omit that indice entirely when it should be replaced with null. For example, it looks like functions ARE handled properly but not undefined in this output:

stringify([1, fn, 2])
"[1,null,2]"
stringify([1, undefined, 2])
"[1,,2]"

Might actually be a symptom of undefined not being handled properly overall. I'm coding this up for practice myself and I have this guard at the top of my recursive function:

if (o === undefined) return undefined

I suppose you'd do this similar to how you're already handling null

@jialihan
Copy link

jialihan commented Jul 7, 2021

what does the string regex handler mean?
Line 14:

return '"' + val.replace(/\\/g, '\\\\').replace('"', '\\"') + '"';

it replaces the single-backslash into triple-backslashes('\'), then make the "" to be \\", any use cases?
For example:

const s = "hello, \"alice\"";

Json.stringnify output :

"\"hello, \\\"alice\\\"\""

Your output:

"\"hello, \\\"alice\"\""

Just want to know how and where do you know the rules of JSON.stringnify does?

@siddharth-sunchu
Copy link

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