JSONは NaN
/ Infinity
/ -Infinity
や Date
型等に対応していない。
しかし、JavaScriptのJSONは、これらを解消する仕組みを持っている。
それは JSON.parse
の第二引数の reviver
とJSON.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
の部分はセキュリティへの配慮が皆無だったので削除しました。