it-swarm.com.de

Wie finde und ersetze ich mehrere Feldwerte mit jq?

In der folgenden JSON-Datei

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

Ich muss nach dem Schlüssel "Name" suchen und seinen Wert an allen Stellen durch "XXX" ersetzen. Welcher jq-Befehl macht das?

9
user2181698

Verwenden von jq basierend auf der Funktion walk (benötigt eine aktuelle Version):

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

Wenn Ihr jq die Funktion walk nicht unterstützt, definieren Sie sie einfach als:

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

Credits: https://github.com/stedolan/jq/issues/96

9
oliv

die Zuweisungsoperationen von jq können eine Aktualisierung an so vielen Orten gleichzeitig durchführen, wie Sie benennen können, und sind für diese Art von Situation vorgesehen. Sie können verwenden

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

um jedes Feld mit dem Namen "name" irgendwo zu finden und den Wert in jedem auf einmal durch "XXXX" zu ersetzen und das resultierende Objekt auszugeben.

Dies ist nur das Beispiel ..|.a? Aus der Dokumentation des rekursiven Abstiegs kombiniert mit Aktualisierungszuweisung .

Es verwendet den Operator rekursiver Abstiegsoperator .. , um jeden einzelnen Wert im Baum zu finden, und zieht dann das Feld "Name" aus jedem von ihnen heraus mit .name , nterdrückt alle Fehler von nicht übereinstimmenden Werten mit ? und aktualisiert dann das Objekt an all diesen Stellen gleichzeitig mit "XXXX" unter Verwendung des pdate) -zuweisungsoperator |= und gibt das neue Objekt aus.

Dies funktioniert unabhängig von der Dateistruktur und aktualisiert jedes Namensfeld überall.


Wenn die Datei immer diese Struktur hat und es sich um die bestimmten "Namens" -Felder handelt, die Sie ändern möchten , können Sie dies auch tun Listen Sie sie einfach auf und weisen Sie sie auch als Gruppe zu:

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

Dies macht die gleiche Zuordnung zu

  1. das Feld "Name" des Objekts der obersten Ebene;
  2. das Feld "Name" von every Objekt im Array "contact"; und
  3. das Feld "Name" jedes Objekts im Array "Gruppe".

alles auf einmal. Dies ist besonders nützlich, wenn die Datei möglicherweise andere Namensfelder enthält, die nicht in Beziehung stehen und die Sie nicht ändern möchten. Es werden nur die drei dort genannten Standorte gefunden und alle gleichzeitig aktualisiert.


Wenn der Wert nur ein Literal ist, wie er hier ist, dann einfache Zuweisung mit = funktioniert auch und speichert Ihnen ein Zeichen: (..|.name?)="XXXX" - Sie möchten dies auch, wenn Ihr Wert wird basierend auf dem gesamten Objekt der obersten Ebene berechnet. Wenn Sie stattdessen den neuen Namen basierend auf dem alten berechnen möchten, müssen Sie -|= Verwenden. Wenn ich nicht sicher bin, was ich verwenden soll, hat |= In den Eckfällen im Allgemeinen ein etwas besseres Verhalten.

Wenn Sie mehrere Ersetzungen durchführen müssen , können Sie diese zusammenfügen:

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

aktualisiert überall die Felder "name" und "lname" und gibt das gesamte aktualisierte Objekt einmal aus.


Einige andere Ansätze, die funktionieren könnten:

  • Sie können auch sehr explizit angeben, mit was Sie auswählen

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

    das findet alles, dann nur die Objekte, dann nur die Objekte, die einen "Namen" haben, dann das Namensfeld für diese Objekte und führt die gleiche Aktualisierung wie zuvor durch.

  • Wenn Sie die Entwicklungsversion von jq ausführen (unwahrscheinlich), kann die Funktion walk auch folgende Aufgabe ausführen: walk(.name?="XXXX"). Alle anderen Versionen funktionieren mit der neuesten Version 1.5.
  • Ein alternatives Multi-Update könnte sein

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

    das findet alles mit einen Namen und setzt dann sowohl "name" als auch "lname" für jedes Objekt mit arithmetische Update-Zuweisung *= und - Zusammenführungsverhalten von + für Objekte .

6
Michael Homer

alternativ jtc basierte Lösung:

bash $ jtc -w'<name>l+0' -u'"XXX"' your.json 
{
   "contact": [
      {
         "id": 111,
         "name": "XXX"
      }
   ],
   "email": "xxx",
   "group": [
      {
         "lname": "YYY",
         "name": "XXX"
      }
   ],
   "lname": "YYY",
   "name": "XXX",
   "pass": "yyy"
}
bash $ 
1
Dmitry L.