保留未定义的 JSON.stringify,否则删除

在执行 JSON.stringify(hash)时如何保留 undefined值?

这里有一个例子:

var hash = {
"name" : "boda",
"email" : undefined,
"country" : "africa"
};


var string = JSON.stringify(hash);


// > '{"name":"boda","country":"africa"}'

电子邮件从 JSON.stringify消失了。

38103 次浏览

Use null instead of undefined.

var hash = {
"name" : "boda",
"email" : null,
"country" : "africa"
};


var string = JSON.stringify(hash);


> "{"name":"boda","email":null,"country":"africa"}"

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.

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"]}

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"
}

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')

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)

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");

JSON does not have an undefined value, but we could write a workaround:

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: