Как объединить 2 объекта JSON из 2 файлов с помощью jq?

124

Я использую инструменты jq (jq-json-processor) в сценарии оболочки для анализа json.

У меня есть 2 файла json и я хочу объединить их в один уникальный файл

Вот содержимое файлов:

file1

{
    "value1": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2"
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        }
    }
}

file2

{
    "status": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value3": "v3"
        },      
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

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

{
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        },
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

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

{
  "ccc": {
    "value2": "v2",
    "value1": "v1"
  },
  "bbb": {
    "value2": "v2",
    "value1": "v1"
  },
  "aaa": {
    "value2": "v2",
    "value1": "v1"
  }
}
{
  "ddd": {
    "value4": 4,
    "value3": "v3"
  },
  "bbb": {
    "value3": "v3"
  },
  "aaa": {
    "value4": 4,
    "value3": "v3"
  }
}

Используя эту команду:

jq -s '.[].value' file1 file2
Janfy
источник
1
Вы пробовали jsontool? github.com/trentm/json
Nano Taboada
Еще нет, посмотрю. Thx
Janfy
Для этого jsonиспользуйте:cat f1 f2 | json --deep-merge
xer0x
где / как получить json@ xer0x?
Gus
@Gus ой, чтобы получить jsonинструмент, перейдите на github.com/trentm/json
xer0x

Ответы:

157

Начиная с версии 1.4 это возможно с помощью *оператора. Когда даны два объекта, он рекурсивно объединит их. Например,

jq -s '.[0] * .[1]' file1 file2

Важно: обратите внимание на -s (--slurp)флаг, который помещает файлы в один массив.

Вы бы получили:

{
  "value1": 200,
  "timestamp": 1382461861,
  "value": {
    "aaa": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3",
      "value4": 4
    },
    "bbb": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3"
    },
    "ccc": {
      "value1": "v1",
      "value2": "v2"
    },
    "ddd": {
      "value3": "v3",
      "value4": 4
    }
  },
  "status": 200
}

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

jq -s '.[0] * .[1] | {value: .value}' file1 file2

Или предположительно несколько более эффективный (потому что он не объединяет никакие другие значения):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2
Симо Киннунен
источник
2
ПРИМЕЧАНИЕ : -sфлаг важен, поскольку без него два объекта не входят в массив.
Джон Моралес
1
@SimoKinnunen Здесь мы объединяем два файла json. Возможно ли иметь 1 переменную json и другой файл json. Я пробовал, но, похоже, у меня не работает!
Jayesh Dhandha
Если отдельные файлы отсортированы по ключу, можно ли сохранить порядок в итоговом файле?
Лев,
@SimoKinnunen Я использую эту команду: «jq -s 'add' config.json other / *. Json», но когда я делаю «cat config.json», он не объединяется. Как я могу вернуть результат в файл?
Ренато Бибиано,
63

Использование jq -s add:

$ echo '{"a":"foo","b":"bar"} {"c":"baz","a":0}' | jq -s add
{
  "a": 0,
  "b": "bar",
  "c": "baz"
}

Это считывает все тексты JSON из stdin в массив ( jq -sделает это), а затем «сокращает» их.

( addопределяется как def add: reduce .[] as $x (null; . + $x);, который выполняет итерацию по значениям входного массива / объекта и добавляет их. Добавление объекта == слияние.)

user2259432
источник
4
Можно ли при таком подходе выполнить рекурсивное слияние (оператор *)?
Дэйв Фостер,
1
@DaveFoster Да, примерно так reduce .[] as $x ({}; . * $x)- см. Также ответ jrib .
Chriki
Это помогло мне объединить два файла json с помощью Ansible и jq. Спасибо.
Фелипе Альварес
35

Кто знает, нужно ли вам это еще, но вот решение.

Как только вы дойдете до --slurpопции, это будет просто!

--slurp/-s:
    Instead of running the filter for each JSON object in the input,
    read the entire input stream into a large array and run the filter just once.

Тогда +оператор сделает то, что вы хотите:

jq -s '.[0] + .[1]' config.json config-user.json

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

FiloSottile
источник
19

Вот версия, которая рекурсивно работает (с использованием *) на произвольном количестве объектов:

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' | jq --slurp 'reduce .[] as $item ({}; . * $item)'
{
  "A": {
    "a": 1,
    "b": 2
  },
  "B": 3
}
jrib
источник
2
Отличный ответ. Хорошо работает при объединении всех файлов в каталоге:jq -s 'reduce .[] as $item ({}; . * $item)' *.json
Джон Элмор 08
7

Во-первых, {"value": .value} можно сократить до {value}.

Во-вторых, опция --argfile (доступная в jq 1.4 и jq 1.5) может представлять интерес, так как позволяет избежать использования опции --slurp.

Собрав их вместе, два объекта в двух файлах можно объединить указанным ниже образом:

$ jq -n --argfile o1 file1 --argfile o2 file2 '$o1 * $o2 | {value}'

Флаг '-n' указывает jq не читать из stdin, поскольку здесь вводятся параметры --argfile.

вершина горы
источник
2
jq устарел --argileдля--slurpfile
Дэвид Грумс
3

Это можно использовать для объединения любого количества файлов, указанных в команде:

jq -rs 'reduce .[] as $item ({}; . * $item)' file1.json file2.json file3.json ... file10.json

или это для любого количества файлов

jq -rs 'reduce .[] as $item ({}; . * $item)' ./*.json

dngray
источник