Como mesclar 2 objetos JSON de 2 arquivos usando jq?

123

Estou usando as ferramentas jq (jq-json-processor) no shell script para analisar o json.

Eu tenho 2 arquivos json e quero mesclá-los em um único arquivo

Aqui o conteúdo dos arquivos:

file1

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

arquivo2

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

Eu tento muitas combinações, mas o único resultado que recebo é o seguinte, que não é o 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
fonte
1
Já experimentou o jsontool? github.com/trentm/json
Nano Taboada
Ainda não, vou dar uma olhada. Thx
Janfy 27/10/2013
Para fazer isso com o jsonuso:cat f1 f2 | json --deep-merge
xer0x 21/11
onde / como você consegue json@ xer0x?
Gus
@Gus oh, para obter o jsonmovimento ferramenta para github.com/trentm/json
xer0x

Respostas:

154

Desde 1.4, isso agora é possível com o *operador. Quando dados dois objetos, eles serão mesclados recursivamente. Por exemplo,

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

Importante: Observe o -s (--slurp)sinalizador, que coloca os arquivos na mesma matriz.

Você:

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

Se você também deseja se livrar das outras chaves (como o resultado esperado), uma maneira de fazer isso é:

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

Ou o presumivelmente um pouco mais eficiente (porque não mescla outros valores):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2
Simo Kinnunen
fonte
2
NOTA : o -ssinalizador é importante, pois sem ele os dois objetos não estão em uma matriz.
19415 John Morales
1
@SimoKinnunen Aqui estamos mesclando dois arquivos json. É possível ter 1 variável json e outro arquivo json. Eu tentei, mas parece que não está funcionando para mim!
Jayesh Dhandha
Se os arquivos individuais forem classificados por uma chave, é possível preservar a ordem no arquivo resultante?
Leo
@SimoKinnunen Estou usando este comando: "jq -s 'add' config.json outro / *. Json", mas quando faço um "cat config.json", ele não é mesclado. Como posso colocar a saída de volta no arquivo?
Renato Bibiano
63

Use jq -s add:

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

Isso lê todos os textos JSON do stdin em uma matriz ( jq -sfaz isso) e depois os "reduz".

( addé definido como def add: reduce .[] as $x (null; . + $x);, que itera sobre os valores da matriz de entrada / objeto e os adiciona. Adição de objeto == mesclagem.)

user2259432
fonte
4
É possível fazer a mesclagem recursiva (operador *) com essa abordagem?
Dave Foster
1
@DaveFoster Sim, com algo como reduce .[] as $x ({}; . * $x)- veja também a resposta de jrib .
Chriki
Isso me ajudou a mesclar dois arquivos json usando Ansible e jq. Obrigado.
Felipe Alvarez
35

Quem sabe se você ainda precisa, mas aqui está a solução.

Depois de chegar à --slurpopção, é 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.

Em seguida, o +operador fará o que você deseja:

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

(Nota: se você deseja mesclar objetos internos em vez de substituir apenas os arquivos da esquerda pelos arquivos da direita, será necessário fazê-lo manualmente)

FiloSottile
fonte
19

Aqui está uma versão que funciona recursivamente (usando *) em um número arbitrário 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
fonte
2
Ótima resposta. Funciona bem ao mesclar todos os arquivos em um diretório:jq -s 'reduce .[] as $item ({}; . * $item)' *.json
John Ellmore
7

Primeiro, {"value": .value} pode ser abreviado para apenas {value}.

Segundo, a opção --argfile (disponível nas versões 1.4 e 1.5) pode ser interessante, pois evita o uso da opção --slurp.

Juntando isso, os dois objetos nos dois arquivos podem ser combinados da maneira especificada da seguinte maneira:

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

O sinalizador '-n' diz ao jq para não ler no stdin, pois as entradas são provenientes das opções --argfile aqui.

pico
fonte
2
jq foi descontinuado --argilepor--slurpfile
David Groomes 21/03/19
3

Isso pode ser usado para mesclar qualquer número de arquivos especificados no comando:

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

ou isso para qualquer número de arquivos

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

dngray
fonte