Jak połączyć 2 obiekty JSON z 2 plików za pomocą jq?

124

Używam JQ narzędzi (JQ-json-Processor) w skrypcie powłoki do analizowania JSON.

Mam 2 pliki json i chcę je połączyć w jeden unikalny plik

Tutaj zawartość plików:

plik1

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

plik2

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

Spodziewany wynik

{
    "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
        }
    }
}

Próbuję wielu kombinacji, ale jedyny wynik, jaki otrzymuję, jest następujący, co nie jest oczekiwanym wynikiem:

{
  "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"
  }
}

Za pomocą tego polecenia:

jq -s '.[].value' file1 file2
Janfy
źródło
1
Czy próbowałeś już jsontool? github.com/trentm/json
Nano Taboada
Jeszcze nie, spojrzę. Thx
Janfy
Aby to zrobić, jsonużyj:cat f1 f2 | json --deep-merge
xer0x
gdzie / jak otrzymujesz json@ xer0x?
Gus
@Gus oh, aby pobrać jsonnarzędzie, wejdź na github.com/trentm/json
xer0x

Odpowiedzi:

155

Od wersji 1.4 jest to teraz możliwe dzięki *operatorowi. Gdy otrzyma dwa obiekty, połączy je rekurencyjnie. Na przykład,

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

Ważne: Zwróć uwagę na -s (--slurp)flagę, która umieszcza pliki w tej samej tablicy.

Dałoby ci:

{
  "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
}

Jeśli chcesz również pozbyć się innych kluczy (takich jak oczekiwany wynik), możesz to zrobić w następujący sposób:

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

Lub prawdopodobnie nieco bardziej wydajne (ponieważ nie łączy żadnych innych wartości):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2
Simo Kinnunen
źródło
2
UWAGA : -sflaga jest ważna, ponieważ bez niej dwa obiekty nie znajdują się w tablicy.
John Morales,
1
@SimoKinnunen Tutaj łączymy dwa pliki json. Czy można mieć 1 zmienną json i inny plik json. Próbowałem, ale wydaje mi się, że to nie działa!
Jayesh Dhandha
Jeśli poszczególne pliki są posortowane według klucza, czy można zachować kolejność w pliku wynikowym?
Leo
@SimoKinnunen Używam tego polecenia: "jq -s 'add' config.json other / *. Json", ale kiedy robię "cat config.json", nie jest ono scalane. Jak mogę przywrócić dane wyjściowe do pliku?
Renato Bibiano
63

Zastosowanie jq -s add:

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

To czyta wszystkie teksty JSON ze standardowego wejścia do tablicy ( jq -srobi to), a następnie „redukuje” je.

( addjest zdefiniowany jako def add: reduce .[] as $x (null; . + $x);, który iteruje po wartościach tablicy wejściowej / obiektu i dodaje je. Dodawanie obiektu == scal).

user2259432
źródło
4
Czy przy takim podejściu można wykonać rekursywne scalanie (operator *)?
Dave Foster
1
@DaveFoster Tak, z czymś w rodzaju reduce .[] as $x ({}; . * $x)- zobacz także odpowiedź jrib .
Chriki
Pomogło mi to scalić dwa pliki json przy użyciu Ansible i jq. Dzięki.
Felipe Alvarez
35

Kto wie, czy nadal go potrzebujesz, ale oto rozwiązanie.

Gdy już znajdziesz się w tej --slurpopcji, jest to łatwe!

--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.

Wtedy +operator zrobi, co chcesz:

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

(Uwaga: jeśli chcesz scalić wewnętrzne obiekty zamiast po prostu nadpisywać lewe pliki prawymi, musisz to zrobić ręcznie)

FiloSottile
źródło
19

Oto wersja, która działa rekurencyjnie (używając *) na dowolnej liczbie obiektów:

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' | jq --slurp 'reduce .[] as $item ({}; . * $item)'
{
  "A": {
    "a": 1,
    "b": 2
  },
  "B": 3
}
jrib
źródło
2
Świetna odpowiedź. Działa dobrze podczas łączenia wszystkich plików w katalogu:jq -s 'reduce .[] as $item ({}; . * $item)' *.json
John Ellmore,
7

Po pierwsze, {"wartość": .value} można skrócić do po prostu {wartość}.

Po drugie, opcja --argfile (dostępna w jq 1.4 i jq 1.5) może być interesująca, ponieważ pozwala uniknąć konieczności używania opcji --slurp.

Składając je razem, dwa obiekty w dwóch plikach można połączyć w określony sposób w następujący sposób:

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

Flaga '-n' mówi jq, aby nie czytał ze standardowego wejścia, ponieważ dane wejściowe pochodzą z opcji --argfile tutaj.

szczyt
źródło
2
jq jest przestarzały --argiledla--slurpfile
David Groomes
3

Można to wykorzystać do scalenia dowolnej liczby plików określonych w poleceniu:

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

lub to dla dowolnej liczby plików

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

dngray
źródło