Preserving undefined that JSON.stringify otherwise removes
JavascriptJsonJavascript Problem Overview
How do I preserve undefined
values when doing JSON.stringify(hash)
?
Here's an example:
var hash = {
"name" : "boda",
"email" : undefined,
"country" : "africa"
};
var string = JSON.stringify(hash);
// > '{"name":"boda","country":"africa"}'
Email disappeared from JSON.stringify
.
Javascript Solutions
Solution 1 - Javascript
The JSON spec does not allow undefined
values, but does allow null
values.
You can pass a replacer function to JSON.stringify
to automatically convert undefined
values to null
values, like this:
var string = JSON.stringify(
obj,
function(k, v) { return v === undefined ? null : v; }
);
This works for undefined values inside arrays as well, as JSON.stringify
already converts those to null
.
Solution 2 - Javascript
You can preserve the key by converting to null
since a valid JSON does not allow undefined
;
Simple one liner:
JSON.stringify(obj, (k, v) => v === undefined ? null : v)
Solution 3 - Javascript
This should do the trick
// Since 'JSON.stringify' hides 'undefined', the code bellow is necessary in
// order to display the real param that have invoked the error.
JSON.stringify(hash, (k, v) => (v === undefined) ? '__undefined' : v)
.replace(/"__undefined"/g, 'undefined')
Solution 4 - Javascript
Use null
instead of undefined
.
var hash = {
"name" : "boda",
"email" : null,
"country" : "africa"
};
var string = JSON.stringify(hash);
> "{"name":"boda","email":null,"country":"africa"}"
Solution 5 - Javascript
function stringifyWithUndefined(obj, space) {
const str = JSON.stringify(obj, (key, value) => value === undefined ? '__undefined__' : value, space);
return str.replace(/"__undefined__"/g, 'undefined');
}
Example:
const obj = {
name: 'boda',
email: undefined,
country: 'africa'
};
console.log(stringifyWithUndefined(obj, 2));
Result:
{
"name": "boda",
"email": undefined,
"country": "africa"
}
Solution 6 - Javascript
Im reading between the lines here and guessing that you want to have the value undefined when you use JSON.parse?
If that is the case you could use the following:
var encodeUndefined = function(obj, undefinedPaths, path) {
path = path || 'ROOT';
for (var key in obj) {
var keyPath = path + '.' + key;
var value = obj[key];
if (value === undefined) {
undefinedPaths.push(keyPath);
} else if (typeof value == "object" && value !== null) {
encodeUndefined(obj[key], undefinedPaths, keyPath);
}
}
}
var stringifyAndPreserveUndefined = function(obj) {
var undefinedPaths = [];
//save all paths that have are undefined in a array.
encodeUndefined((obj), undefinedPaths);
return JSON.stringify({
ROOT: obj,
undefinedPaths: undefinedPaths
}, function(k, v) { if (v === undefined) { return null; } return v; });
}
var parseAndRestoreUndefined = function(value) {
var data = JSON.parse(value);
var undefinedPaths = data.undefinedPaths;
var obj = data.ROOT;
//Restore all undefined values
for (var pathIndex = 0; pathIndex < undefinedPaths.length; pathIndex++) {
var pathParts = undefinedPaths[pathIndex].substring(5).split('.');
var item = obj;
for (var pathPartIndex = 0; pathPartIndex < pathParts.length - 1; pathPartIndex++) {
item = item[pathParts[pathPartIndex]];
}
item[pathParts[pathParts.length - 1]] = undefined;
}
return obj;
}
var input = {
test1: 'a',
test2: 'b',
test3: undefined,
test4: {
test1: 'a',
test2: undefined
}
};
var result = stringifyAndPreserveUndefined(input);
var result2 = parseAndRestoreUndefined(result);
stringifyAndPreserveUndefined
will encode all undefined values in a array and when you call parseAndRestoreUndefined
it will put them in the correct place again.
The one downside is the json will not look exactly like the object. In the example above it will turn into {"ROOT":{"test1":"a","test2":"b","test4":{"test1":"a"}},"undefinedPaths":["ROOT.test3","ROOT.test4.test2"]}
Solution 7 - Javascript
Preserving nested undefined values
I wrote 2 functions that internally uses JSON.stringify
and JSON.parse
and preserves nested undefined
values using a value placeholder:
Equivalent to JSON.stringify
:
/**
* Serialize a POJO while preserving nested `undefined` values.
*/
function serializePOJO(value, undefinedPlaceholder = "[undefined]") {
const replacer = (key, value) => (value === undefined ? undefinedPlaceholder : value);
return JSON.stringify(value, replacer);
}
Equivalent to JSON.parse
:
/**
* Deserialize a POJO while preserving nested `undefined` values.
*/
function deserializePOJO(value, undefinedPlaceholder = "[undefined]") {
const pojo = JSON.parse(value);
if (pojo === undefinedPlaceholder) {
return undefined;
}
// Function that walks through nested values
function deepIterate(value, callback, parent, key) {
if (typeof value === "object" && value !== null) {
Object.entries(value).forEach(([entryKey, entryValue]) => deepIterate(entryValue, callback, value, entryKey));
} else if (Array.isArray(value)) {
value.forEach((itemValue, itemIndex) => deepIterate(itemValue, callback, value, itemIndex));
} else if (parent !== undefined) {
callback(value, parent, key);
}
}
// Replaces `undefined` placeholders
deepIterate(pojo, (value, parent, key) => {
if (value === undefinedPlaceholder) {
parent[key] = undefined;
}
});
return pojo;
}
Usage:
const source = {
foo : undefined,
bar : {
baz : undefined
}
};
const serialized = serializePOJO(source);
console.log("Serialized", serialized);
// '{"foo":"[undefined]","bar":{"baz":"[undefined]","qux":[1,"[undefined]",2]}}'
const deserialized = deserializePOJO(serialized);
console.log("Deserialized", deserialized);
Works with both object entries and array items.
The downside is that you have to choose an appropriate placeholder that will not be mistaken via a "real" source value. The placeholder is customizable via the optional undefinedPlaceholder
argument.
This is specially useful to store POJO in browser local storage ;)
See also:
Solution 8 - Javascript
This will cause it to print as undefined
but this is INVALID json, but is valid JavaScript.
var string = JSON.stringify(obj, function(k,v){return v===undefined?"::undefined::":v}, 2).replace(new RegExp("\"::undefined::\"", 'g'), "undefined");