Jak zaktualizować pojedynczą wartość w dokumencie json przy użyciu jq?

110

Przepraszam, jeśli przeoczyłem coś bardzo oczywistego; Właśnie znalazłem jqi próbuję go użyć do zaktualizowania jednej wartości JSON bez wpływu na otaczające dane.

Chciałbym przesłać curlwynik do jq, zaktualizować wartość i przesłać zaktualizowany kod JSON do pliku curl -X PUT. Coś jak

curl http://example.com/shipping.json | jq '.' field: value | curl -X PUT http://example.com/shipping.json

Do tej pory zhakowałem to razem za pomocą sed, ale po obejrzeniu kilku przykładów |=operatora w jqjestem pewien, że ich nie potrzebuję.

Oto przykład JSON - jak użyłbym jqustawienia "local": false, zachowując resztę JSON?

{
  "shipping": {
    "local": true,
    "us": true,
    "us_rate": {
      "amount": "0.00",
      "currency": "USD",
      "symbol": "$"
    }
  }
}
STW
źródło

Odpowiedzi:

131

Możesz ustawić wartości obiektu za pomocą =operatora. |=z drugiej strony służy do aktualizacji wartości. To subtelna, ale ważna różnica. Kontekst filtrów zmienia się.

Ponieważ ustawiasz właściwość na stałą wartość, użyj =operatora.

.shipping.local = false

Zwróć uwagę, że ustawiając wartość właściwości, niekoniecznie musi ona istnieć. W ten sposób możesz łatwo dodawać nowe wartości.

.shipping.local = false | .shipping.canada = false | .shipping.mexico = true
Jeff Mercado
źródło
10
Ta próbka nie jest dobra dla wartości normalnej. Więc jeśli potrzebujesz zmienić normalną wartość, musisz ją dodać ", na przykład .shipping.local = "new place". Więc całe polecenie będzie curl http://example.com/shipping.json | jq '.shipping.local = "new place"'. W przeciwnym razie otrzymasz dziwne błędy.
BMW,
9
@BMW co? Tu jest doskonale. Każda prawidłowa wartość json jest prawidłowa, tak się składa, że ​​jest to literał false. Wartości nie muszą być łańcuchami.
Jeff Mercado,
@BMW, z którym OP chce to ustawić false. Co jest nie tak?
SOFe
Jak ustawić, .shipping.<INSERT VAR HERE>gdzie VAR jest zdefiniowane w jq? Na prosty przykład, jak zmodyfikowałbyś jq --arg location local '.shipping.$location = false'go, aby działał?
Max Murphy
1
@MaxMurphy:.shipping[$location] = false
Jeff Mercado
21

Zaktualizuj wartość (ustawia .foo.bar na „nową wartość”):

jq '.foo.bar = "new value"' file.json

Zaktualizuj wartość za pomocą zmiennej (ustawia .foo.bar na „hello”):

variable="hello"; jq --arg variable "$variable" '.foo.bar = $variable' file.json
Paul Chris Jones
źródło
Użyłem tego jako przykładu, aby zmienić nazwę pakietu NPM.json przez powłokę i zadziałało w 100%, dzięki.
Machado
3
To faktycznie nie zastępuje zawartości pliku. Drukuje tylko na standardowe wyjście. Będziesz musiał zapisać stout z powrotem do pliku, aby zmiana została zachowana. Zobacz stackoverflow.com/a/60744617/1626687
spuder
3

podobną funkcją do operatora | = jest mapowanie. map będzie odpowiednia, aby uniknąć wymogu poprzedniego filtru dla tablicy ...

wyobraź sobie, że Twoje dane są tablicą (bardzo często w tym przykładzie)

[
  {
    "shipping": {
      "local": true,
      "us": true,
      "us_rate": {
        "amount": "1.00",
        "currency": "USD",
        "symbol": "$"
      }
    }
  },
  {
    "shipping": {
      "local": true,
      "us": true,
      "us_rate": {
        "amount": "1.00",
        "currency": "USD",
        "symbol": "$"
      }
    }
  }
]

stąd konieczne jest rozważenie tablicy w kodzie jako:

http://example.com/shipping.json | jq '.[] | .shipping.local = "new place"' | curl -X PUT http://example.com/shipping.json

lub użyć funkcji mapy, która została stworzona do pracy w każdym elemencie tablicy jako

http://example.com/shipping.json | jq 'map(.shipping.local = "new place")' | curl -X PUT http://example.com/shipping.json

Obserwacja

Ze względu na tych, którzy się uczą, popełniłeś również błędy w używaniu jq, po prostu weź pod uwagę, że "odczytuje" pierwszy parametr jako program, stąd wszystkie pożądane polecenia powinny być zawarte w pierwszym ciągu po wywołaniu funkcji program.

Thiago Conrado
źródło
Dlaczego to z konieczności różni się od .[].shipping.local = "new place"?
roaima
1
różnica w tym przypadku jest odpowiedzią, użycie funkcji map da tablicę obiektów, podczas gdy w innej opcji otrzymasz sekwencję obiektów, po jednym w wierszu (przydatne do integracji z innymi interpretatorami jak bash)
Thiago Conrado