Пересечение 2 json объектов с помощью jq

Вопрос или проблема

У меня есть первая структура json. Все значения всегда равны null.

{
    "A": {
        "A1": {
            "A11": null,
            "A12": null
        }
    },
    "B": {
        "B1": {
            "B12": null
        },
        "B2": null
    },
    "C": {
        "C1": {
            "C11": null,
            "C12": null
        },
        "C2" : {
            "C21": {
                "C211": null
            }
        }
    }
}

Во второй структуре json все значения всегда являются строками.

{
    "A": {
        "A1": {
            "A11": "valueA11",
        }
    },
    "B": {
        "B1": {
            "B12": "valueB12",
            "B13": "valueB13"
        },
        "B2": "valueB2"
    },
    "C": "valueC"
}

Результат будет таким:

{
    "A": {
        "A1": {
            "A11": "valueA11",
            "A12": null
        }
    },
    "B": {
        "B1": {
            "B12": "valueB12"
        },
        "B2": "valueB2"
    },
    "C": {
        "C1": {
            "C11": null,
            "C12": null
        },
        "C2" : {
            "C21": {
                "C211": null
            }
        }
    }
}

Как мне заполнить ключи первого json значениями из второго, если ключи существуют во втором json, в противном случае мы оставляем значения null?

Я пытался использовать jq в bash, но без успеха. Я готов использовать любой инструмент, который справится с задачей.

Итерация с помощью reduce по путям из paths(. == null), получение соответствующего значения во втором input с использованием getpath, и установка его с помощью setpath или возвращение к текущему значению итерации с помощью ? // ..

jq 'input as $in |
  reduce paths(. == null) as $p (.; setpath($p; $in | getpath($p))? // .)
' first.json second.json
{
  "A": {
    "A1": {
      "A11": "valueA11",
      "A12": null
    }
  },
  "B": {
    "B1": {
      "B12": "valueB12"
    },
    "B2": "valueB2"
  },
  "C": {
    "C1": {
      "C11": null,
      "C12": null
    },
    "C2": {
      "C21": {
        "C211": null
      }
    }
  }
}

Демо

Ответ или решение

Для решения задачи по объединению двух JSON-структур с использованием утилиты jq, мы можем воспользоваться подходом, который позволяет заполнять значения из второго JSON в первом, если ключи совпадают. В первом JSON все значения равны null, а во втором – это строки. Наша цель – обновить первый JSON, сохраняя структуру объектов и заполняя только те ключи, которые имеют соответствия во втором JSON.

Исходные JSON-структуры выглядят следующим образом:

Первый JSON (ключи с null значениями):

{
    "A": {
        "A1": {
            "A11": null,
            "A12": null
        }
    },
    "B": {
        "B1": {
            "B12": null
        },
        "B2": null
    },
    "C": {
        "C1": {
            "C11": null,
            "C12": null
        },
        "C2": {
            "C21": {
                "C211": null
            }
        }
    }
}

Второй JSON (ключи со строковыми значениями):

{
    "A": {
        "A1": {
            "A11": "valueA11"
        }
    },
    "B": {
        "B1": {
            "B12": "valueB12",
            "B13": "valueB13"
        },
        "B2": "valueB2"
    },
    "C": "valueC"
}

Результатом, который вы ожидаете получить, будет:

{
    "A": {
        "A1": {
            "A11": "valueA11",
            "A12": null
        }
    },
    "B": {
        "B1": {
            "B12": "valueB12"
        },
        "B2": "valueB2"
    },
    "C": {
        "C1": {
            "C11": null,
            "C12": null
        },
        "C2": {
            "C21": {
                "C211": null
            }
        }
    }
}

Чтобы добиться такого результата, вы можете использовать следующую команду на bash с утилитой jq:

jq 'input as $in |
  reduce paths(. == null) as $p (.; setpath($p; $in | getpath($p))? // .)' first.json second.json

Объяснение команды:

  1. input as $in – сохраняем второй JSON во временную переменную $in.
  2. reduce paths(. == null) as $p – проходим по всем путям в первом JSON, где значение равно null.
  3. setpath($p; $in | getpath($p))? // . – для каждого найденного пути $p, пытаемся установить значение из второго JSON (если оно существует), и если его нет, оставляем текущее значение (то есть null).

Таким образом, данный подход позволяет элегантно и эффективно объединять обе структуры, сохраняя при этом необходимые значения и структуру первого JSON.

Для тестирования и демонстрации данного решения, можете воспользоваться данной ссылкой на jqPlay.

Оцените материал
Добавить комментарий

Капча загружается...