¿Cómo fusionar 2 objetos JSON de 2 archivos usando jq?

124

Estoy usando las herramientas jq (jq-json-processor) en el script de shell para analizar json.

Tengo 2 archivos json y quiero combinarlos en un archivo único

Aquí el contenido de los archivos:

archivo1

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

archivo2

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

Resultado Esperado

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

Intento muchas combinaciones, pero el único resultado que obtengo es el siguiente, que no es el resultado esperado:

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

Usando este comando:

jq -s '.[].value' file1 file2
Janfy
fuente
1
¿Has probado jsontool? github.com/trentm/json
Nano Taboada
Todavía no, echaré un vistazo. Thx
Janfy
Para hacer esto con el jsonuso:cat f1 f2 | json --deep-merge
xer0x
¿Dónde / cómo se obtiene json@ xer0x?
Gus
@Gus oh, para obtener la jsonherramienta, vaya a github.com/trentm/json
xer0x

Respuestas:

155

Desde 1.4 esto ahora es posible con el *operador. Cuando se le dan dos objetos, los fusionará de forma recursiva. Por ejemplo,

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

Importante: tenga en cuenta la -s (--slurp)bandera, que coloca los archivos en la misma matriz.

Te conseguiría:

{
  "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 también desea deshacerse de las otras claves (como su resultado esperado), una forma de hacerlo es esta:

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

O el presumiblemente algo más eficiente (porque no combina ningún otro valor):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2
Simo Kinnunen
fuente
2
NOTA : la -sbandera es importante ya que sin ella los dos objetos no están en una matriz.
John Morales
1
@SimoKinnunen Aquí estamos fusionando dos archivos json. ¿Es posible tener 1 variable json y otro archivo json? ¡Lo intenté pero parece que no funciona para mí!
Jayesh Dhandha
Si los archivos individuales están ordenados por una clave, ¿es posible conservar el orden en el archivo resultante?
Leo
@SimoKinnunen Estoy usando este comando: "jq -s 'add' config.json other / *. Json" pero cuando hago un "cat config.json" no se fusiona. ¿Cómo puedo devolver la salida al archivo?
Renato Bibiano
63

Utilizar jq -s add:

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

Esto lee todos los textos JSON de stdin en una matriz ( jq -shace eso) y luego los "reduce".

( addse define como def add: reduce .[] as $x (null; . + $x);, que itera sobre los valores de la matriz de entrada / objeto y los agrega. Suma de objetos == fusionar).

usuario2259432
fuente
4
¿Es posible hacer la combinación recursiva (operador *) con este enfoque?
Dave Foster
1
@DaveFoster Sí, con algo como reduce .[] as $x ({}; . * $x), vea también la respuesta de jrib .
Chriki
Esto me ayudó a fusionar dos archivos json usando Ansible y jq. Gracias.
Felipe Alvarez
35

Quién sabe si todavía lo necesita, pero aquí está la solución.

Una vez que llegue a la --slurpopción, ¡es fácil!

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

Entonces el +operador hará lo que quiera:

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

(Nota: si desea fusionar objetos internos en lugar de simplemente sobrescribir los del archivo izquierdo con los del archivo derecho, deberá hacerlo manualmente)

FiloSottile
fuente
19

Aquí hay una versión que funciona de forma recursiva (usando *) en una cantidad arbitraria de objetos:

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' | jq --slurp 'reduce .[] as $item ({}; . * $item)'
{
  "A": {
    "a": 1,
    "b": 2
  },
  "B": 3
}
jrib
fuente
2
Gran respuesta. Funciona bien al fusionar todos los archivos en un directorio:jq -s 'reduce .[] as $item ({}; . * $item)' *.json
John Ellmore
7

Primero, {"value": .value} se puede abreviar a solo {value}.

En segundo lugar, la opción --argfile (disponible en jq 1.4 y jq 1.5) puede ser de interés ya que evita tener que usar la opción --slurp.

Al juntarlos, los dos objetos de los dos archivos se pueden combinar de la forma especificada de la siguiente manera:

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

La bandera '-n' le dice a jq que no lea desde stdin, ya que las entradas provienen de las opciones --argfile aquí.

pico
fuente
2
jq ha quedado obsoleto --argilepor--slurpfile
David Groomes
3

Esto se puede usar para fusionar cualquier número de archivos especificados en el comando:

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

o esto para cualquier cantidad de archivos

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

dngray
fuente