Skip to content

Instantly share code, notes, and snippets.

@youkinjoh
Last active March 9, 2022 07:37
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save youkinjoh/94fccb393c90bf3945510ec450c0fe14 to your computer and use it in GitHub Desktop.
Save youkinjoh/94fccb393c90bf3945510ec450c0fe14 to your computer and use it in GitHub Desktop.
JSONが対応していない値(NaN/Infinity/Date等)をJSONに保存する方法

JSONが対応していない値をJSONに保存する方法

JSONは NaN / Infinity / -InfinityDate 型等に対応していない。 しかし、JavaScriptのJSONは、これらを解消する仕組みを持っている。 それは JSON.parse の第二引数の reviverJSON.stringify の第二引数の replacer だ。

注意が必要なのは、 Date 型は toJSON メソッドを持つため replacer に値が渡る前に文字列になってしまうこと。 これに対応するには toJSON メソッドを一時退避してしまえば良い。

これらを上手く駆使すれば、JSONに型を保ったまま値を保存し復元することができる。

こんな感じ。

var original = {
  'nan': NaN,
  '+inf': Number.POSITIVE_INFINITY,
  '-inf': Number.NEGATIVE_INFINITY,
  'date': new Date(),
  'symbol': Symbol.for('key'),
}

var replacer = function(key, value) {
  switch (true) {
  case Number.isNaN(value):
    return {
      __extendData__: true,
      type: 'number',
      value: 'NaN',
    };
  case Number.POSITIVE_INFINITY === value:
    return {
      __extendData__: true,
      type: 'number',
      value: 'Infinity',
    };
  case Number.NEGATIVE_INFINITY === value:
    return {
      __extendData__: true,
      type: 'number',
      value: '-Infinity',
    };
  case value instanceof Date:
    return {
      __extendData__: true,
      type: 'date',
      value: value.toISOString(),
    };
  case typeof value === 'symbol':
    return {
      __extendData__: true,
      type: 'symbol',
      value: Symbol.keyFor(value),
    };
  }
  return value;
};

var toJSON = Date.prototype.toJSON;
delete Date.prototype.toJSON;
var serialized = JSON.stringify(original, replacer);
Date.prototype.toJSON = toJSON;

var reviver = function(key, value) {
  if (typeof(value) !== 'object' || !value.__extendData__) {
    return value;
  }
  switch(value.type) {
  case 'number':
    switch(value.value) {
    case 'NaN': return Number.NaN;
    case 'Infinity': return Number.POSITIVE_INFINITY;
    case '+Infinity': return Number.POSITIVE_INFINITY;
    case '-Infinity': return Number.NEGATIVE_INFINITY;
    }
  case 'date':
    return new Date(value.value);
  case 'symbol':
    return Symbol.for(value.value);
  }
};

var deserialized = JSON.parse(serialized, reviver);

console.log(original);
console.log(serialized);
console.log(deserialized);

※ 公開当時にあった function の部分はセキュリティへの配慮が皆無だったので削除しました。

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