0

I have a json file like this:

{  "active": "secure",  "secure": {   "nodetype": "secure",   "nodeid": null,   "servers": [    "ts2.na",    "ts1.na",    "ts3.na"   ],   "stakeaddr": null,   "email": null,   "fqdn": null,   "ipv": "4"  } }

I wanted to change it to something like this:

 "active": "secure",
 "secure": {
  "nodetype": "secure",
  "nodeid": null,
  "servers": [
   "ts2.na",
   "ts1.na",
   "ts3.na"
  ],
  "stakeaddr": "my nicea address",
  "email": "mynice@email.com",
  "fqdn": "itsmyfqdn",
  "ipv": "4"
 }
}

So, I tried this

jq '.secure.stakeaddr = "my nice address"' config.json

although it gives me this output:

{ 
        "stakeaddr": "my nice address",
        "email": null,
        "fqdn": null,
    }

But, when I cat config.json, it shows the last unchanged file. I tried with chmod 777 as well, same result. I do not know what I am doing wrong.

3

jq does not do in-place edits.

Instead:

cp config.json config.json.tmp &&
jq '.secure.stakeaddr = "my nice address"' config.json.tmp >config.json &&
rm config.json.tmp

This copies the file to a temporary file, applies the modifications to that (if the copying was successful) and redirects the result to the original filename. Then you remove the temporary file (if the jq call was successful).

This would give you a new config.json file that looked like

{
  "active": "secure",
  "secure": {
    "nodetype": "secure",
    "nodeid": null,
    "servers": [
      "ts2.na",
      "ts1.na",
      "ts3.na"
    ],
    "stakeaddr": "my nice address",
    "email": null,
    "fqdn": null,
    "ipv": "4"
  }
}

To insert a value from a shell variable:

$ fqdn='Then she said "hello"'
$ jq --arg fqdn "$fqdn" '.secure.fqdn = $fqdn' file.json
{
  "active": "secure",
  "secure": {
    "nodetype": "secure",
    "nodeid": null,
    "servers": [
      "ts2.na",
      "ts1.na",
      "ts3.na"
    ],
    "stakeaddr": null,
    "email": null,
    "fqdn": "Then she said \"hello\"",
    "ipv": "4"
  }
}

That is, use --arg variable value to pass the value into jq, and then $variable within the jq expression. Notice that doing it this way will properly encode the value. Injecting the shell variable's value directly into the expression would not encode its value, and would possibly give rise to errors or a malformed JSON document.

  • Is there any for it to read global variable, like this? jq '.secure.fqdn = "$FQDN"' config.json.tmp >config.json But it didnt work for me. – Rakib Fiha Feb 5 at 11:49
  • @RakibFiha Sure, I'll add something about that soon (I'm not at a computer at the moment). – Kusalananda Feb 5 at 12:12
  • Cheers, eagerly looking forward to it. thank you. – Rakib Fiha Feb 5 at 12:26
  • @RakibFiha Updated. – Kusalananda Feb 5 at 12:42
  • 1
    @RakibFiha With your example: jq --arg key1 "$FQDN" '.secure.fqdn = $key1'. You should additionally double quote $FQDN as I have done here. – Kusalananda Feb 5 at 12:50
1

If you're not limited to jq only, here's an easy alternative - jtc allowing in-place modifications.

Assuming your source json is in file config.json then, for example, adding "stakeaddr": "my nice address" would look like this:

bash $ jtc -w'[secure]' -mu'{ "stakeaddr": "my nice address" }' -f config.json 
bash $ cat config.json
{
   "active": "secure",
   "secure": {
      "email": null,
      "fqdn": null,
      "ipv": "4",
      "nodeid": null,
      "nodetype": "secure",
      "servers": [
         "ts2.na",
         "ts1.na",
         "ts3.na"
      ],
      "stakeaddr": "my nice address"
   }
}
bash $

there's a number of ways to achieve the same. given the label stakeaddr already exist in the source json, then a better way would be:

bash $ jtc -w'[secure][stakeaddr]' -u'"my nice address"' -f config.json
  • Thank you Dmitry, this one seems very good way of doing it as well. I will try it. Thank you again. – Rakib Fiha Feb 5 at 15:39
0

Considering you have python, which comes pre-installed in most *nix distributions, and the json file containing your data is named file.json, then following script

import json
from pprint import pprint

with open('file.json', 'r') as f:
    jsonData = json.load(f)
pprint(jsonData)

would print a result similar to following:

{'active': 'secure',
 'secure': {'email': None,
            'fqdn': None,
            'ipv': '4',
            'nodeid': None,
            'nodetype': 'secure',
            'servers': ['ts2.na', 'ts1.na', 'ts3.na'],
            'stakeaddr': None}}
  • But, I am implementing this in shell scripting, that jq command as @kusalanada showed in the comment can take multiple input in single line. Works really well in bash. – Rakib Fiha Feb 5 at 14:19
  • Problem is that it will create a temporary file to append it, which is fine I guess considering the simplicity. – Rakib Fiha Feb 5 at 14:20
  • @RakibFiha A for loop can be added, along with command-line arg parsing, to pick multiple json files, with just a bit of additional code, if required. Would u like me to edit answer for that ? – programmer Feb 5 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.