サーバーサイドの急な仕様変更でJSONを変換する機会があったのでメモ。

内容としてはAPIから受け取ったJSONをコピーしたり、キーの記法(キャメルとかスネークとか)を変換したり、値を独自関数で変換するというもの。
プロジェクトによっては阿鼻叫喚な内容だが一時的な対応かつリクエスト・レスポンスの見直しも含めてだったので止む無く即席で作りました。
ソースコードは以下。

目次

コピー

const cloneDeep = value => {
  if (Array.isArray(value)) return value.map(v => cloneDeep(v))
  if (typeof value === 'object' && value !== null) {
    const obj = {}
    for (const [k, v] of Object.entries(value)) {
      obj[k] = cloneDeep(v)
    }
    return obj
  }
  return value
}

調べてみるとよく出てくるのはJSON.parseJSON.stringifyの組み合わせやLodashcloneDeepでしたが、今回はJS内で扱うオブジェクトではなく、あくまで整形前のJSONなので上のコードで特に問題はない認識です。(結局後ほどLodashの使用許可が下りたので無駄になった分はスタッフがおいしく頂きました🍙)

キー変換(toキャメルケース)

const toCamelCase = value => {
  if (Array.isArray(value)) return value.map(v => cloneDeep(v))
  if (typeof value === 'object' && value !== null) {
    const obj = {}
    for (const [k, v] of Object.entries(value)) {
      obj[_.camelCase(k)] = cloneDeep(v)
    }
    return obj
  }
  return value
}

ケースの変換にはLodash使用してます。
なんだかんだ突貫工事の割には一番有能だった。

値変換

const transformValueDeep = (value, transform = v => v, ...keys) => {
  if (Array.isArray(value)) return value.map(v => transformValueDeep(v, transform, ...keys))
  if (typeof value === 'object' && value !== null) {
    const obj = {}
    for (const [k, v] of Object.entries(value)) {
      obj[k] = keys.includes(k) ? cloneDeep(transform(v)) : cloneDeep(v)
    }
    return obj
  }
  return value
}

const json = { id: 1, item: { item_id: 1 } }
const tranformedJson = transformValueDeep(json, v => `${v}`, 'id', 'item_id')

第二引数には値を変換させるコールバック関数を渡す。 第三引数以降は変換対象のキーが続いていくが、あまりにも多い場合は配列にしてしまう方がベター。

値変換(複数)

const transformValuesDeep = (value, ...transforms) => {
  if (Array.isArray(value)) return value.map(v => transformValuesDeep(v, ...transforms))
  if (typeof value === 'object' && value !== null) {
    const obj = {}
    for (const [k, v] of Object.entries(value)) {
      if (!transforms.length) {
        obj[k] = cloneDeep(v)
        continue
      }
      for (const { transform, keys } of transforms) {
        obj[k] = keys.includes(k) ? cloneDeep(transform(v)) : cloneDeep(v)
      }
    }
    return obj
  }
  return value
}

const json = { name: '', date: '....' }
const tranformedJson = transformValuesDeep(json, { transform: v => `Name: ${v}`, keys: ['name'] }, { transform: v => new Date(v), keys: ['date'] })

先ほどの関数の複数形。
「キーAfunc1で変換」「キーBfunc2で変換」みたいにキーによって変換の関数を変えたい問題が多発したので作成。

cloneDeepの箇所はLodash_.cloneDeepでも問題ないはず。
ただもしコピペして使う際はダミーデータでしっかり検証してから各環境でベンチマーク取ることをお勧めします。
スナック菓子を啄むぐらいの軽さで書いてるけど、多分いろんな箇所に影響が出るような処理なので🐛

プロフィール画像

ふじわら

よくわからないもので戯れてたら自分のことすらよくわからない人間になってしまいました。

ひっそりYouTubeしてます。