Como encontrar e substituir vários valores de campo usando jq?

4

No seguinte arquivo json,

{
  "email": "xxx",
  "pass": "yyy",
  "contact": [
    {
      "id": 111,
      "name": "AAA"
    }
  ],
  "lname": "YYY",
  "name": "AAA",
   "group": [
    {
      "name": "AAA",
      "lname": "YYY",
    }
  ],

Eu preciso procurar a chave "name" e substituir seu valor por "XXX" em todos os lugares. Qual comando jq faz isso?

    
por user2181698 19.10.2018 / 14:12

2 respostas

6

Usando jq com base na função walk (precisa de uma versão recente):

jq 'walk(.name?="XXX")' file

Se o seu jq não suportar a função walk , apenas defina-o como:

jq '
  # Apply f to composite entities recursively, and to atoms
  def walk(f):
    . as $in
    | if type == "object" then
       reduce keys[] as $key
         ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
    elif type == "array" then map( walk(f) ) | f
    else f
    end;
  walk(.name?="XXX")
' file

Créditos: link

    
por 19.10.2018 / 15:02
1

As operações de atribuição do jq podem executar uma atualização em tantos locais ao mesmo tempo quanto você pode nomear e são feitos para esse tipo de situação. Você pode usar

jq '(.. | .name?) |= "XXXX"'

para encontre cada campo chamado "nome" em qualquer lugar e substitua o valor de cada um de uma vez por "XXXX", e imprima o objeto resultante.

Este é apenas o exemplo ..|.a? da documentação de descendência recursiva combinada com < href="https://stedolan.github.io/jq/manual/#Update-assignment:%7C="> atualizar tarefa .

Ele usa o operador de descendente recursivo .. para encontrar todos os valores na árvore Em seguida, extrai o campo "nome" de cada um deles com .name , suprime os erros de valores não correspondentes com ? e em seguida, atualiza o objeto em todos os lugares de uma vez com "XXXX" usando o operador de atribuição de atualização |= e gera o novo objeto.

Isso funcionará independentemente da estrutura do arquivo e atualizará todos os campos de nomes em todos os lugares.

Como alternativa, se o arquivo sempre tiver essa estrutura e esses campos específicos de "nome" que você deseja alterar , não apenas qualquer nome antigo, você também poderá listá-los e atribuir a eles como um grupo também:

jq '(.name, .contact[].name, .group[].name) |= "XXXX"'

Isso faz a mesma atribuição para

  1. o campo "nome" do objeto de nível superior;
  2. o campo "nome" do objeto todo na matriz "contato"; e
  3. o campo "nome" de todos os objetos na matriz "grupo".

tudo de uma só vez. Isso é particularmente útil se o arquivo puder ter campos de nome outros em algum lugar não relacionado que você não queira alterar. Ele encontra apenas os três conjuntos de locais lá nomeados e atualiza todos eles simultaneamente.

Se o valor for apenas um literal como aqui, então atribuição simples com = também funciona e economiza um caractere: (..|.name?)="XXXX" - você também deseja isso se seu valor for calculado com base no objeto de nível superior inteiro. Se, em vez disso, você quiser calcular o novo nome com base no nome antigo, precisará usar |= . Se não tenho certeza do que usar, |= geralmente tem um comportamento ligeiramente melhor nos casos de canto.

Se você tiver várias substituições para fazer , você pode canalizá-las:

jq '(..|.name?) = "XXXX" | (..|.lname?) = "1234"'

atualizará os campos "nome" e "lname" em todos os lugares e exibirá todo o objeto atualizado uma vez.

Algumas outras abordagens que podem funcionar:

  • Você também pode ser muito explícito sobre o que está selecionando com

    (..|objects|select(has("name"))).name |= "XXXX"'
    

    que encontra tudo, depois apenas os objetos, apenas os objetos que possuem um "nome", o campo de nome nesses objetos e a mesma atualização de antes.

  • Se você estiver executando a versão de desenvolvimento do jq (improvável), a walk function também pode fazer o trabalho: walk(.name?="XXXX") . Todas as outras versões funcionarão na última versão lançada, 1.5.
  • Uma atualização múltipla alternativa pode ser

    jq '(..|has("name")?) += {name: "XXXX", lname: "1234"}'
    

    que encontra tudo com um nome e depois define "name" e "lname" em cada objeto usando atribuição-atualização aritmética *= e o corção de comportamento que + tem para objetos .

por 20.10.2018 / 09:07

Tags