Using jq to change json object and field value from variable

北城以北 提交于 2021-01-28 04:42:09

问题


I want to modify the following json using variables specified in a Linux bash shell using jq.

var1="red"
var2="european.flowers"
var3="european_vegetables"
var4="20"

My json:

{
 "plants": {
  "flowers.small": {
    "colour": "",
    "age": "",
    "vegetables": {
     "root": "",
     "height": ""
    }
  }
 }
}

I want to modify the json using variables in jq:

{
 "plants": {
  "${var2}": {
    "colour": "${var1}",
    "age": "",
    "${var3}": {
     "root": "",
     "height": "${var4}"
    }
  }
 }
}

I am attempting to just set a field value from a variables:

Command:

cat myjson.json|jq '.plants["flowers.small"].colour = "${var1}"' -c

The result is:

{"plants":{"flowers.small":{"colour":"${var1}","age":"","vegetables":{"root":"","height":""}}}}

Command:

cat myjson.json|jq --arg v "$var1" '.plants.["flowers.small"].colour = [$v]' -c

The result is:

jq: error: syntax error, unexpected '[', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.plants.["flowers.small"].colour = $v
jq: 1 compile error

My jq version is: jq-1.5-1-a5b5cbe

How can I rename a field and set a value for the key from the variables? Is this even doable using the jq version?


回答1:


Using jq as a template engine

If you don't really need input.json to be a separate file, the easy thing to do is to define your entire template as a jq expression:

var1="red"
var2="european.flowers"
var3="european_vegetables"
var4="20"
jq -n --arg var1 "$var1" --arg var2 "$var2" --arg var3 "$var3" --arg var4 "$var4" '
{
 "plants": {
  "\($var2)": {
    "colour": $var1,
    "age": "",
    "\($var3)": {
     "root": "",
     "height": $var4
    }
  }
 }
}'

emits as output:

{
  "plants": {
    "european.flowers": {
      "colour": "red",
      "age": "",
      "european_vegetables": {
        "root": "",
        "height": "20"
      }
    }
  }
}

Alternately: Renaming subtrees

If you really do want to rename an existing key, consider an approach like the below. Using jq variables for the template fields isn't mandatory, but it does make it easier to change your code later:

var1="red"; var2="european.flowers"; var3="european_vegetables"; var4="20"

jq --arg var1 "$var1" --arg var2 "$var2" --arg var3 "$var3" --arg var4 "$var4" '

  # set variables for the fields we expect to see in our input
  "flowers.small" as $plant_tmpl |
  "vegetables" as $cat_tmpl |

  # change things inside fields we will later rename *before* we rename those fields
  .plants[$plant_tmpl].colour = $var1 |
  .plants[$plant_tmpl][$cat_tmpl].height = $var4 |

  if $var3 == $cat_tmpl then . else
    # var3 is not "vegetables" (templated value), so we need to rename it
    .plants[$plant_tmpl][$var3] = .plants[$plant_tmpl][$cat_tmpl] |
    del(.plants[$plant_tmpl][$cat_tmpl])
  end |

  if $var2 == $plant_tmpl then . else
    .plants[$var2] = .plants[$plant_tmpl] |
    del(.plants[$plant_tmpl])
  end
' <<'EOF'
{
 "plants": {
  "flowers.small": {
    "colour": "",
    "age": "",
    "vegetables": {
     "root": "",
     "height": ""
    }
  }
 }
}
EOF



回答2:


I suspect this is very close to what you want:

#!/bin/bash

var1="red"
var2="european.flowers"
var3="european_vegetables"
var4="20"

jq --arg var1 "$var1" \
   --arg var2 "$var2" \
   --arg var3 "$var3" \
   --arg var4 "$var4" '
   {"${var1}": $var1, "${var2}": $var2, "${var3}": $var3, "${var4}": $var4} as $dict
   | walk( if type == "string" and $dict[.] then  $dict[.] 
           elif type=="object" then with_entries(if $dict[.key] then .key |= $dict[.] else . end)
           else . end)' template.json

This general approach is OK, but you might want to check the jq Cookbook for suggestions about using jq as a template engine: https://github.com/stedolan/jq/wiki/Cookbook#using-jq-as-a-template-engine

Streamlined version

If your jq supports $ARGS, then you could use this streamlined version of the above:

jq --arg '${var1}' "$var1" \
   --arg '${var2}' "$var2" \
   --arg '${var3}' "$var3" \
   --arg '${var4}' "$var4" '
    $ARGS.named as $dict
    | walk( if type == "string" and $dict[.] then  $dict[.] 
            elif type=="object" then with_entries(if $dict[.key] then .key |= $dict[.] else . end)
            else . end)' template.json


来源:https://stackoverflow.com/questions/54601240/using-jq-to-change-json-object-and-field-value-from-variable

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!