Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change entry in a JSON list that matches a condition without discarding rest of document

Tags:

json

bash

edit

jq

I am trying to open a file, look through the file and change a value based on the value and pass this either to a file or var. Below is an example of the JSON

{
     "Par": [
         {
             "Key": "12345L",
             "Value": "https://100.100.100.100:100",
             "UseLastValue": true
         },
         {
             "Key": "12345S",
             "Value": "VAL2CHANGE",
             "UseLastValue": true
         },
         {
             "Key": "12345T",
             "Value": "HAPPY-HELLO",
             "UseLastValue": true
         }
     ],
      "CANCOPY": false,
      "LOGFILE": ["HELPLOG"]
 }

i have been using jq and i have been successful in isolating the object group and change the value.

 cat jsonfile,json | jq '.Par | map(select(.Value=="VAL2CHANGE")) | .[] | .Value="VALHASBEENCHANGED"'

This gives

         {
             "Key": "12345S",
             "Value": "VALHASBEENCHANGED",
             "UseLastValue": true
         }

What id like to achieve is to retain the full JSON output with the changed value

{
     "Par": [
         {
             "Key": "12345L",
             "Value": "https://100.100.100.100:100",
             "UseLastValue": true
         },
         {
             "Key": "12345S",
             "Value": "VALHASBEENCHANGED",
             "UseLastValue": true
         },
         {
             "Key": "12345T",
             "Value": "HAPPY-HELLO",
             "UseLastValue": true
         }
     ],
      "CANCOPY": false,
      "LOGFILE": ["HELPLOG"]
 }

I.E.

jq '.Par | map(select(.Value=="VAL2CHANGE")) | .[] | .Value="VALHASBEENCHANGED"' (NOW PUT IT BACK IN FILE)

OR

open file, look in file, file value to be changed and change this and output this to a file or to screen

To add, the json file will only contain the value im looking for once as im creating this. If any other values need changing i will name differently.

like image 851
BobMonk Avatar asked Dec 23 '22 05:12

BobMonk


2 Answers

jq --arg match "VAL2CHANGE" \
   --arg replace "VALHASBEENCHANGED" \
  '.Par |= map(if .Value == $match then (.Value=$replace) else . end)' \
  <in.json

To more comprehensively replace a string anywhere it may be in a nested data structure, you can use the walk function -- which will be in the standard library in jq 1.6, but can be manually pulled in in 1.5:

jq --arg match "VAL2CHANGE" \
   --arg replace "VALHASBEENCHANGED" '

# taken from jq 1.6; will not be needed here after that version is released.
# Apply f to composite entities recursively, and to atoms
def walk(f):
  . as $in
  | if type == "object" then
      reduce keys_unsorted[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

walk(if . == $match then $replace else . end)' <in.json
like image 134
Charles Duffy Avatar answered Jan 11 '23 17:01

Charles Duffy


If you're just replacing based on the values, you could stream the file and replace the values as you rebuild the result.

$ jq --arg change 'VAL2CHANGE' --arg value 'VALHASBEENCHANGED' -n --stream '
fromstream(inputs | if length == 2 and .[1] == $change then .[1] = $value else . end)
' input.json
like image 36
Jeff Mercado Avatar answered Jan 11 '23 17:01

Jeff Mercado