2013-10-22 13 views
53

Ich verwende die jq Werkzeuge (jq-json-Prozessor) in Shell-Skript, um JSON zu analysieren.Wie 2 json-Datei mit jq zusammenführen?

Ich habe 2 json Dateien und wollen, dass sie in einer einzigen Datei

Hier wird der Inhalt von Dateien fusionieren:

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

erwartetes Ergebnis

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

Ich versuche viel combinaison aber das einzige Ergebnis, das ich bekommen ist die folgende, die nicht das erwartete Ergebnis ist:

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

Mit diesem Befehl:

jq -s '.[].value' file1 file2 
+1

Haben Sie versucht jsontool? https://github.com/trentm/json –

+0

Noch nicht, ich werde einen Blick darauf werfen. Thx – Janfy

+0

Um dies mit 'Json' zu tun, benutzen Sie:' cat f1 f2 | json --deep-merge' – xer0x

Antwort

50

1.4 Da dies nun möglich, mit dem * Operator ist. Wenn zwei Objekte angegeben werden, werden sie rekursiv zusammengeführt.Zum Beispiel

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

Werden Sie erhalten:

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

Wenn Sie auch von den anderen Tasten loswerden wollen (wie Ihr erwartetes Ergebnis), eine Möglichkeit, es zu tun, ist dies:

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

Oder die vermutlich etwas effizienter (weil es keine anderen Werte nicht fusionieren):

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

_NOTE_: Das '-s' Flag ist wichtig, da ohne es die beiden Objekte nicht in einem Array sind. –

20

Wer weiß, ob Sie es noch brauchen, aber hier ist die Lösung.

Sobald Sie die Option --slurp erreichen, ist es einfach!

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

Dann wird der + Operator wird tun, was Sie wollen:

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

(Hinweis: Wenn Sie inneren Objekte zusammenführen möchten, anstatt nur mit den richtigen Datei diejenigen, die linke Datei zu überschreiben, werden Sie müssen es manuell tun)

30

Verwendung jq -s add:

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

Dies liest alle JSON-Texte von stdin in ein Array (jq -s macht das) und "reduziert" sie dann.

(add als def add: reduce .[] as $x (null; . + $x); definiert, der iteriert über dem Eingangsfeld/der Objektwerte und addiert sie. Addierobjekt == merge).

+4

Ist es möglich, die rekursive Zusammenführung (* Operator) mit diesem Ansatz zu tun? –

+0

@DaveFoster Ja, mit etwas wie 'reduce. [] Als $ x ({};. * $ X)' - siehe auch [jribs Antwort] (http://stackoverflow.com/a/36218044/1797912). – Chriki

2

Zunächst kann {"value": .value} zu nur {value} abgekürzt werden.

Zweitens kann die Option --argfile (verfügbar in jq 1.4 und jq 1.5) von Interesse sein, da die Option --slurp nicht verwendet werden muss.

diese zusammen Einlochen, können die beiden Objekte in den beiden Dateien in der angegebenen Weise wie folgt kombiniert werden:

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

Hier ist eine Version, die rekursiv (mit *) auf einer beliebigen Anzahl von Objekten funktioniert:

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' | jq --slurp 'reduce .[] as $item ({}; . * $item)' 
{ 
    "A": { 
    "a": 1, 
    "b": 2 
    }, 
    "B": 3 
}