【JavaScript】JSON.stringify/parse をカスタマイズしてDate型とかを使えるようにする

JSON を拡張して Date 型とかも使えるようにしたい。

そういうライブラリはすでにある(ejson)けど、標準の JSON.stringify() の第二引数を上手に使えば、ライブラリに頼らなくても自前で簡単にできる。

JSON.stringify() の第二引数には関数を与えることができて、そこで値をいろいろ変更できる。

それを利用すると、こんなふうに JSON の拡張を実装できる。

const CustomJSON = {
  replacer (key, value) {
    if (this[key] instanceof Date) {
      return {
        $type: 'Date',
        $value: this[key].getTime(),
      }
    }
    return value  
  },
  reviver (key, value) {
    if (value && value.$type === 'Date') {
      return new Date(value.$value)
    }
    return value  
  }
}

const json = JSON.stringify(
  {
    number: 1,
    string: 'a',
    date: new Date(),
  },
  CustomJSON.replacer,
  '  ',
)
console.log(json)
// {
//   "number": 1,
//   "string": "a",
//   "date": {
//     "$type": "Date",
//     "$value": 1530797362183
//   }
// }

const obj = JSON.parse(json, CustomJSON.reviver)
console.log(obj)
// {
//   number: 1,
//   string: 'a',
//   date: 2018-07-05T13:29:22.183Z
// }

注意点として、replacer(key, value)value は、.toJSON() が呼び出されたあとの値が渡される。なので、Date 型を渡すと value には date.toJSON() (つまり文字列)が入っている。

そこで、渡された値が Date 型であることを判定するには this[key] を使用している。thiskey を持つオブジェクト(valueの一階層上のオブジェクト)を指している。

この方法を使えば、カスタムクラスとかの JSON シリアライズもわりと簡単にできそうだ。