JSONのマージ方法
2025/05/20
6分で読める

JSONのマージ方法

様々なプログラミング言語でJSONオブジェクトをマージするための包括的ガイド

JSONマージの紹介

JSONオブジェクトのマージは、データ処理、設定管理、APIインタラクションにおける基本的な操作です。ユーザー設定の結合、設定ファイルのマージ、APIレスポンスの集約など、JSONオブジェクトを適切にマージする方法を理解することは、現代の開発において非常に重要です。

JSONマージとは、2つ以上のJSONオブジェクトを単一の統合オブジェクトに結合するプロセスを指します。フラットなオブジェクトの場合、このプロセスは単純かもしれませんが、ネストされた構造、配列、競合する値を扱う場合はより複雑になります。

基本的なJSONマージの概念

実装の詳細に入る前に、JSONマージに関連するいくつかの重要な概念を理解しておくことが重要です:

浅いマージと深いマージ

  • 浅いマージ:最上位のプロパティのみが結合されます。両方のオブジェクトが同じプロパティを含む場合、2番目のオブジェクトの値が最初のオブジェクトの値を上書きします。
  • 深いマージ:マージ操作はオブジェクトツリーを再帰的に走査し、ネストされたオブジェクトを置き換えるのではなく結合します。

競合する値のマージ戦略

JSONオブジェクトをマージする際、競合する値はいくつかの方法で処理できます:

戦略説明ユースケース
後者優先最後のオブジェクトの値が以前の値を上書きするデフォルト設定
先者優先最初のオブジェクトの値が保持されるユーザー設定の保持
カスタムロジック異なるプロパティに特定のロジックを適用する複雑なビジネスルール
競合時エラー競合が検出された場合にエラーを発生させる重要なデータの整合性

JavaScriptでJSONをマージする方法

JavaScriptには、JSONオブジェクトをマージするためのいくつかの組み込みメソッドがあります:

Object.assign()の使用

Object.assign()メソッドは浅いマージを実行し、ソースオブジェクトのすべての列挙可能な独自プロパティをターゲットオブジェクトにコピーします。

const json1 = { name: "John", age: 30 };
const json2 = { city: "New York", age: 31 };

const merged = Object.assign({}, json1, json2);
console.log(merged);
// 出力: { name: "John", city: "New York", age: 31 }

同じキーを持つプロパティは、パラメータリストで後に来るオブジェクトによって上書きされることに注意してください。

スプレッド演算子(...)の使用

スプレッド演算子は、オブジェクトをマージするためのより簡潔な方法を提供します:

const json1 = { name: "John", age: 30 };
const json2 = { city: "New York", age: 31 };

const merged = { ...json1, ...json2 };
console.log(merged);
// 出力: { name: "John", city: "New York", age: 31 }

このメソッドも、重複するキーを後のオブジェクトの値で上書きします。

JSONオブジェクトの深いマージ

Object.assign()とスプレッド演算子は浅いマージを実行します。ネストされたオブジェクトの場合、深いマージの実装が必要です:

再帰的な深いマージ関数

function deepMerge(target, source) {
  const output = Object.assign({}, target);
  
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
        if (!(key in target)) {
          Object.assign(output, { [key]: source[key] });
        } else {
          output[key] = deepMerge(target[key], source[key]);
        }
      } else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  
  return output;
}

function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

// 使用例
const json1 = { 
  name: "John", 
  address: { 
    city: "New York", 
    zip: 10001 
  } 
};

const json2 = { 
  name: "Jane", 
  address: { 
    state: "NY" 
  } 
};

const merged = deepMerge(json1, json2);
console.log(merged);
// 出力: { 
//   name: "Jane", 
//   address: { 
//     city: "New York", 
//     zip: 10001, 
//     state: "NY" 
//   } 
// }

ライブラリを使用したJSONのマージ

より複雑なマージシナリオでは、確立されたライブラリの使用を検討してください:

LodashのmergeとmergeWith

Lodashは深いマージのための堅牢な関数を提供します:

const _ = require('lodash');

const json1 = { user: { name: "John", data: [1, 2] } };
const json2 = { user: { age: 30, data: [3, 4] } };

// 基本的な深いマージ
const merged1 = _.merge({}, json1, json2);
console.log(merged1);
// 出力: { user: { name: "John", age: 30, data: [3, 4] } }

// mergeWithを使用したカスタムマージ
const merged2 = _.mergeWith({}, json1, json2, (objValue, srcValue) => {
  if (Array.isArray(objValue)) {
    return objValue.concat(srcValue);
  }
});
console.log(merged2);
// 出力: { user: { name: "John", age: 30, data: [1, 2, 3, 4] } }

deepmergeパッケージ

deepmerge npmパッケージは、深いマージのために特別に設計されています:

const deepmerge = require('deepmerge');

const json1 = { user: { name: "John", hobbies: ["reading"] } };
const json2 = { user: { age: 30, hobbies: ["swimming"] } };

// デフォルトマージ(配列を連結)
const merged = deepmerge(json1, json2);
console.log(merged);
// 出力: { user: { name: "John", age: 30, hobbies: ["reading", "swimming"] } }

// カスタム配列マージ
const overwriteMerge = (destinationArray, sourceArray) => sourceArray;
const options = { arrayMerge: overwriteMerge };
const mergedCustom = deepmerge(json1, json2, options);
console.log(mergedCustom);
// 出力: { user: { name: "John", age: 30, hobbies: ["swimming"] } }

他の言語でのJSONマージ

Python

組み込みの辞書更新メソッドを使用:

import json

json1_str = '{"name": "John", "age": 30}'
json2_str = '{"city": "New York", "age": 31}'

# JSON文字列を辞書に解析
json1 = json.loads(json1_str)
json2 = json.loads(json2_str)

# 辞書をマージ
merged = {**json1, **json2}  # Python 3.5+

# JSON文字列に戻す
merged_json = json.dumps(merged)
print(merged_json)
# 出力: {"name": "John", "city": "New York", "age": 31}

Pythonでの深いマージ:

def deep_merge(dict1, dict2):
    result = dict1.copy()
    for key, value in dict2.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = deep_merge(result[key], value)
        else:
            result[key] = value
    return result

json1 = {"user": {"name": "John", "settings": {"theme": "dark"}}}
json2 = {"user": {"age": 30, "settings": {"notifications": True}}}

merged = deep_merge(json1, json2)
print(merged)
# 出力: {'user': {'name': 'John', 'settings': {'theme': 'dark', 'notifications': True}, 'age': 30}}

Ruby

Hash#mergeメソッドを使用:

require 'json'

json1_str = '{"name": "John", "age": 30}'
json2_str = '{"city": "New York", "age": 31}'

# JSON文字列をハッシュに解析
json1 = JSON.parse(json1_str)
json2 = JSON.parse(json2_str)

# ハッシュをマージ
merged = json1.merge(json2)

# JSON文字列に戻す
merged_json = JSON.generate(merged)
puts merged_json
# 出力: {"name":"John","city":"New York","age":31}

Rubyでの深いマージ:

require 'json'

# Rubyの組み込みdeep_merge
require 'active_support/core_ext/hash/deep_merge'

json1 = JSON.parse('{"user": {"name": "John", "settings": {"theme": "dark"}}}')
json2 = JSON.parse('{"user": {"age": 30, "settings": {"notifications": true}}}')

merged = json1.deep_merge(json2)
puts JSON.generate(merged)
# 出力: {"user":{"name":"John","settings":{"theme":"dark","notifications":true},"age":30}}

特殊なマージケース

配列のマージ

配列を含むJSONオブジェクトをマージする場合、いくつかの戦略があります:

  1. 置換:後の配列が前の配列を完全に置き換える
  2. 連結:両方の配列の要素を結合する
  3. インデックスによるマージ:同じ位置の配列要素をマージする
  4. IDによるマージ:識別子フィールドに基づいて配列要素をマージする
// 配列連結の例
const json1 = { tags: ["important", "urgent"] };
const json2 = { tags: ["completed", "archived"] };

const merged = {
  ...json1,
  tags: [...json1.tags, ...json2.tags]
};
console.log(merged);
// 出力: { tags: ["important", "urgent", "completed", "archived"] }

// IDによる配列マージの例
const users1 = { users: [{ id: 1, name: "John" }, { id: 2, name: "Jane" }] };
const users2 = { users: [{ id: 1, age: 30 }, { id: 3, name: "Bob", age: 25 }] };

function mergeArraysById(arr1, arr2, idKey) {
  const merged = [...arr1];
  
  arr2.forEach(item2 => {
    const item1Index = merged.findIndex(item1 => item1[idKey] === item2[idKey]);
    
    if (item1Index >= 0) {
      merged[item1Index] = { ...merged[item1Index], ...item2 };
    } else {
      merged.push(item2);
    }
  });
  
  return merged;
}

const mergedUsers = {
  users: mergeArraysById(users1.users, users2.users, 'id')
};

console.log(mergedUsers);
// 出力: {
//   users: [
//     { id: 1, name: "John", age: 30 },
//     { id: 2, name: "Jane" },
//     { id: 3, name: "Bob", age: 25 }
//   ]
// }

nullとundefined値の処理

オブジェクトをマージする際、nullundefined値の処理方法を決める必要があります:

const json1 = { name: "John", age: null, city: undefined };
const json2 = { age: 30 };

// デフォルトの動作(null値はコピーされ、undefinedは無視される)
const merged1 = Object.assign({}, json1, json2);
console.log(merged1);
// 出力: { name: "John", age: 30 }

// 深いマージでのカスタム処理
function customDeepMerge(target, source) {
  const output = Object.assign({}, target);
  
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      // ソースのnull値をスキップ
      if (source[key] === null) return;
      
      if (isObject(source[key])) {
        if (!(key in target)) {
          output[key] = source[key];
        } else {
          output[key] = customDeepMerge(target[key], source[key]);
        }
      } else {
        output[key] = source[key];
      }
    });
  }
  
  return output;
}

JSONをマージするためのコマンドラインツール

jqの使用

jqは、JSONファイルをマージできる強力なコマンドラインJSONプロセッサです:

# 2つのJSONファイルをマージ
jq -s '.[0] * .[1]' file1.json file2.json > merged.json

# カスタム配列処理を伴う深いマージ
jq -s '.[0] * .[1] | .array = (.[0].array + .[1].array)' file1.json file2.json > merged.json

Node.jsの使用

Node.jsでJSONファイルをマージするための簡単なスクリプトを作成できます:

const fs = require('fs');
const _ = require('lodash');

// JSONファイルを読み込む
const file1 = JSON.parse(fs.readFileSync('file1.json', 'utf8'));
const file2 = JSON.parse(fs.readFileSync('file2.json', 'utf8'));

// オブジェクトをマージ
const merged = _.merge({}, file1, file2);

// 結果を書き込む
fs.writeFileSync('merged.json', JSON.stringify(merged, null, 2));

JSONマージのベストプラクティス

  1. 重複キーについて明示的にする:選択した方法がキーの競合をどのように処理するかを理解する
  2. 不変性を考慮する:既存のオブジェクトを変更するのではなく、新しいオブジェクトを作成する
  3. 深いマージを慎重に処理する:ネストされたオブジェクトには適切な再帰的メソッドまたはライブラリを使用する
  4. マージされた結果を検証する:最終オブジェクトが有効な構造を持っていることを確認する
  5. エッジケースでテストする:空のオブジェクト、null値、深くネストされた構造
  6. マージ戦略を文書化する:競合がどのように解決されるかを明確にする
  7. パフォーマンスを考慮する:大きなオブジェクトの場合、一部の深いマージ実装は非効率的かもしれない

結論

JSONオブジェクトのマージは一般的ですが微妙な操作です。適切なマージ戦略は、特定の要件、データ構造、言語環境によって異なります。様々なマージ技術とその意味を理解することで、データの整合性を維持し、アプリケーションのニーズを満たしながら、異なるソースからのデータを効果的に結合することができます。

著者

avatar for Corey
Corey

ニュースレター

コミュニティに参加

最新ニュースとアップデートのためにニュースレターを購読してください