Preserving undefined that JSON.stringify otherwise removes

JavascriptJson

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbodacydoView Question on Stackoverflow
Solution 1 - JavascriptFlimmView Answer on Stackoverflow
Solution 2 - JavascriptchickensView Answer on Stackoverflow
Solution 3 - JavascriptZizacoView Answer on Stackoverflow
Solution 4 - JavascriptEfogView Answer on Stackoverflow
Solution 5 - Javascripttanguy_kView Answer on Stackoverflow
Solution 6 - JavascriptPeterView Answer on Stackoverflow
Solution 7 - JavascriptYves M.View Answer on Stackoverflow
Solution 8 - JavascriptDustin PoissantView Answer on Stackoverflow