Comment fusionner 2 objets JSON à partir de 2 fichiers en utilisant jq?

124

J'utilise les outils jq (jq-json-processor) dans le script shell pour analyser json.

J'ai 2 fichiers json et je souhaite les fusionner en un seul fichier

Voici le contenu des fichiers:

fichier1

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

fichier2

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

résultat attendu

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

J'essaye beaucoup de combinaisons mais le seul résultat que j'obtiens est le suivant, ce qui n'est pas le résultat attendu:

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

En utilisant cette commande:

jq -s '.[].value' file1 file2
Janfy
la source
1
Avez-vous essayé jsontool? github.com/trentm/json
Nano Taboada
Pas encore, je vais jeter un oeil. Thx
Janfy
Pour ce faire, jsonutilisez:cat f1 f2 | json --deep-merge
xer0x
où / comment obtenez-vous json@ xer0x?
Gus
@Gus oh, pour obtenir l' jsonoutil, rendez-vous sur github.com/trentm/json
xer0x

Réponses:

155

Depuis la version 1.4, cela est désormais possible avec l' *opérateur. Lorsqu'on lui donne deux objets, il les fusionnera récursivement. Par exemple,

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

Important: notez l' -s (--slurp)indicateur, qui place les fichiers dans le même tableau.

Vous obtiendriez:

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

Si vous souhaitez également vous débarrasser des autres clés (comme le résultat attendu), une façon de le faire est la suivante:

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

Ou probablement un peu plus efficace (car il ne fusionne aucune autre valeur):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2
Simo Kinnunen
la source
2
REMARQUE : l' -sindicateur est important car sans lui les deux objets ne sont pas dans un tableau.
John Morales
1
@SimoKinnunen Ici, nous fusionnons deux fichiers json. Est-il possible d'avoir 1 variable json et un autre fichier json. J'ai essayé mais cela ne semble pas fonctionner pour moi!
Jayesh Dhandha
Si les fichiers individuels sont triés par clé, est-il possible de conserver l'ordre dans le fichier résultant?
Leo
@SimoKinnunen J'utilise cette commande: "jq -s 'add' config.json autre / *. Json" mais quand je fais un "cat config.json", il n'est pas fusionné. Comment puis-je remettre la sortie dans le fichier?
Renato Bibiano le
63

Utilisez jq -s add:

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

Cela lit tous les textes JSON de stdin dans un tableau ( jq -sfait cela) puis il les "réduit".

( addest défini comme def add: reduce .[] as $x (null; . + $x);, qui itère sur les valeurs du tableau d'entrée / objet et les ajoute. Ajout d'objet == merge.)

user2259432
la source
4
Est-il possible de faire la fusion récursive (opérateur *) avec cette approche?
Dave Foster
1
@DaveFoster Oui, avec quelque chose comme reduce .[] as $x ({}; . * $x)- voir aussi la réponse de jrib .
Chriki
Cela m'a aidé à fusionner deux fichiers json en utilisant Ansible et jq. Merci.
Felipe Alvarez
35

Qui sait si vous en avez encore besoin, mais voici la solution.

Une fois que vous arrivez à l' --slurpoption, c'est facile!

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

Ensuite, l' +opérateur fera ce que vous voulez:

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

(Remarque: si vous souhaitez fusionner des objets internes au lieu de simplement écraser ceux du fichier de gauche par ceux du fichier de droite, vous devrez le faire manuellement)

FiloSottile
la source
19

Voici une version qui fonctionne de manière récursive (en utilisant *) sur un nombre arbitraire d'objets:

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' | jq --slurp 'reduce .[] as $item ({}; . * $item)'
{
  "A": {
    "a": 1,
    "b": 2
  },
  "B": 3
}
jrib
la source
2
Très bonne réponse. Fonctionne bien lors de la fusion de tous les fichiers dans un répertoire:jq -s 'reduce .[] as $item ({}; . * $item)' *.json
John Ellmore
7

Premièrement, {"value": .value} peut être abrégé en seulement {value}.

Deuxièmement, l'option --argfile (disponible dans jq 1.4 et jq 1.5) peut être intéressante car elle évite d'avoir à utiliser l'option --slurp.

En les mettant ensemble, les deux objets des deux fichiers peuvent être combinés de la manière spécifiée comme suit:

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

Le drapeau '-n' dit à jq de ne pas lire depuis stdin, puisque les entrées proviennent des options --argfile ici.

de pointe
la source
2
jq est obsolète --argiledepuis--slurpfile
David Groomes
3

Cela peut être utilisé pour fusionner n'importe quel nombre de fichiers spécifié dans la commande:

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

ou ceci pour n'importe quel nombre de fichiers

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

dngray
la source