Объединение объектов JSON путем объединения общих полей

#json #merge #jq

#json #слияние #jq

Вопрос:

У меня есть 2 файла локали, и я хочу сгруппировать разные слова в один и тот же ключ

file1 en.json

 {
    "name": "Apple",
    "error": {
        "payment": {
            "value1": "v1"
        }
    }
}
 

файл2 zh.json

 {
    "name": "orange",
    "error": {
        "payment": {
            "value1": "v2",
            "value2": "v5"
        }
    }
}
 

ожидаемый результат

 {
    "name": "Applenorange",
    "error": {
        "payment": {
            "value1": "v1nv2",
            "value2": "v5"
        }
    }
}
 

Я пытаюсь jq -s . en.json zh.json | jq '.[]'| jq -s , но он не может объединяться по ключу.

 {
  "name": "orange",
  "error": {
    "payment": {
      "value1": "v2",
      "value2": "v5"
    }
  }
}
 

Комментарии:

1. Содержат ли ваши фактические входные данные поля с целочисленными / логическими значениями?

Ответ №1:

Насколько я понимаю, проблема, которую вы пытаетесь решить, заключается в том, что вы берете набор json, который соответствует некоторой структуре, и вы хотите собрать все значения, установленные на одном пути json, и создать объект с агрегированными значениями.

Для общего подхода, предполагающего, что в этой структуре не будет массивов, вы можете выделить все скалярные значения в наборе объектов (в этом помогут потоки), сгруппировать их по пути, а затем восстановить структуру объекта.

 $ jq -n '
[inputs | tostream | select(length==2)] #1
  | group_by(.[0]) #2
  | map({
      path: .[0][0],
      values: [.[][1]]
    }) #3
  | reduce .[] as {$path, $values} (
      {};
      setpath($path; $values | join("n"))
    ) #4
' en.json zh.json
 

Обратите внимание на -n флаг.

  1. Преобразует объекты в массив пар путь / значение из потока
  2. Группирует пары по пути
  3. Сопоставляет сгруппированные пары с чем-то более читаемым (не обязательно)
  4. Создает новый объект на основе группировок. Также принимает массив значений и объединяет их в виде одной n строки с разделителями. (как вам было нужно в ожидаемом результате)

https://jqplay.org/s/Aa2KoeS2M6

Ответ №2:

Логика преобразования, которое вы имеете в виду, ускользает от меня, но, учитывая входные данные в вашем примере, следующее дает требуемый результат и может быть легко расширено различными способами:

 jq -n '
  input as $one
  | input as $two
  | reduce ($two | paths(scalars)) as $p  ($one;
      getpath($p) as $v
      | ($two | getpath($p)) as $x
      | if $v
        then setpath($p; "($v)n($x)")
        else setpath($p; $x)
        end)
' en.json zh.json                   
 

Ответ №3:

Или аналогично, тот же запрос может быть выполнен с использованием jtc — unix JSON processor:

 bash $ <en.json jtc -mi zh.json / -w'<>i:' -u'<>i:' -T'"{$a}n{$b}"'
{
   "error": {
      "payment": {
         "value1": "v1nv2",
         "value2": "v5"
      }
   },
   "name": "Applenorange"
}
 

— Первая цепочка ( -mi zh.json ) рекурсивно объединяет оба JSON;
— вторая цепочка ( -w'<>i:' -u'<>i:' -T'"{$a}n{$b}"' ) объединяет скаляры, полученные в результате слияния

PS. так получилось, что я также являюсь создателем jtc .
PPS. вышеуказанный отказ от ответственности требуется SO.