Do you have ideas on what I can change to make this work for nested examples?
Of course, but you will have to abandon the entire function, so I hope you do not mind. I will give a list of letters, why this approach is necessary, and yours, in essence, is wrong from get-go :(
Corner cases
This function performs a simple case analysis for the non-null data constructor property and encodes accordingly. He manages to cover many angular cases that you are unlikely to consider, for example
JSON.stringify(undefined) returns undefinedJSON.stringify(null) returns 'null'JSON.stringify(true) returns 'true'JSON.stringify([1,2,undefined,4]) returns '[1,2,null,4]'JSON.stringify({a: undefined, b: 2}) returns '{ "b": 2 }'JSON.stringify({a: /foo/}) returns { "a": {} }
So, to make sure our stringifyJSON function works fine, I will not test its output directly. Instead, I will write a small test method that ensures that the JSON.parse our encoded JSON does indeed return the original input value
Disclaimer: It should be obvious that the code I'm going to use is not intended to be used as an actual replacement for JSON.stringify - there are probably countless cases where we probably did not address. Instead, this code is used to provide a demonstration of how we could deal with such a task. Additional corner cases can be added to this function.
Runnable demo
Without further ado, here is stringifyJSON in a runnable demo that checks for superior compatibility for a few common cases
const stringifyJSON = data => { if (data === undefined) return undefined else if (data === null) return 'null' else if (data.constructor === String) return '"' + data.replace(/"/g, '\\"') + '"' else if (data.constructor === Number) return String(data) else if (data.constructor === Boolean) return data ? 'true' : 'false' else if (data.constructor === Array) return '[ ' + data.reduce((acc, v) => { if (v === undefined) return [...acc, 'null'] else return [...acc, stringifyJSON(v)] }, []).join(', ') + ' ]' else if (data.constructor === Object) return '{ ' + Object.keys(data).reduce((acc, k) => { if (data[k] === undefined) return acc else return [...acc, stringifyJSON(k) + ':' + stringifyJSON(data[k])] }, []).join(', ') + ' }' else return '{}' }
"So why is it better?"
- this works not only for array types - we can create strings, numbers, arrays, objects, arrays of numbers, arrays of objects, objects containing arrays of strings, even
null and undefined s, etc. - you get the idea - each instance of our
stringifyJSON object stringifyJSON like a small program that tells us how to encode each type (e.g. String , Number , Array , Object , etc.) - no whacked out
typeof type checking - after checking the cases of undefined and null we know that we can try to read the constructor property. - there is no manual cycle where we must mentally track counter variables, how / when to increase them
- no complex
if conditions using && , || ! or checking things like x > y.length , etc. - do not use
obj[0] or obj[i] , which emphasizes our brain - no assumptions that arrays / objects are empty - and without checking the
length property - no other mutations in this regard - this means that we do not need to think about any value of the returned master
resarray or what state it causes after push calls at different stages of the program
Custom objects
JSON.stringify allows us to set the toJSON property on our custom objects, so that when we format them, we get the desired result.
const Foo = x => ({ toJSON: () => ({ type: 'Foo', value: x }) }) console.log(JSON.stringify(Foo(5)))
We could easily add this functionality to our code above - changes to bold
const stringifyJSON = data => { if (data === undefined) return undefined else if (data === null) return 'null' else if (data.toJSON instanceof Function) return stringifyJSON(data.toJSON()) ... else return '{}' } test({toJSON: () => ({a:1, b:2})})
naomik Mar 09 '17 at 8:22 2017-03-09 08:22
source share