How to find and replace json with shell variables using jq? - json

I have read properties with jq from a json object and have stored them to variables.
I want to now read these variables and essentially find and replace a word inside the string with a global shell variable.
I've set my json ID's from my JSON file
# Set Json ID's
TARGET_ID=$(jq '.DefaultCacheBehavior.TargetOriginId' distconfig.json)
DOMAIN_NAME=$(jq '.Origins.Items[0].DomainName' distconfig.json)
ORIGIN_ID=$(jq '.Origins.Items[0].Id' distconfig.json)
echo "$TARGET_ID"
echo "$DOMAIN_NAME"
echo "$ORIGIN_ID"
This returns
"S3-Website-stag4.example.io.s3-website.us-east-2.amazonaws.com"
"stag4.example.io.s3-website.us-east-2.amazonaws.com"
"S3-Website-stag4.example.io.s3-website.us-east-2.amazonaws.com"
I have my location id variable and would like to write it to find and replace all stag4 references in those 3 ID's.
Then I would like to write those 3 ID's to the initial json object, or create a temp version of it.
Example, if:
$DOMAIN_NAME is"stag4.example.io.s3-website.us-east-2.amazonaws.com"
I would like to essentially have it set to:
$LOCATION_NAME="stag6"
DOMAIN_LOCATION="example.io"
"$DOMAIN_NAME=S3-Website-\$LOCATION_NAME\.example.io.s3-website.us-east-2.amazonaws.com"
"$TARGET_ID=\$LOCATION_NAME\.example.io.s3-website.us-east-2.amazonaws.com"
"$ORIGIN_ID=S3-Website-\$LOCATION_NAME\.example.io.s3-website.us-east-2.amazonaws.com"
Then write those 3 to the temp or new json file so I can run my cloudformation command:
aws cloudfront create-distribution --distribution-config file://disttemp.json
I have now built out the proper variables from the initial json file like so:
$LOCATION_NAME="stag6"
DOMAIN_LOCATION="example.io"
echo "Build New IDs"
TARGET_ID_BUILT="S3-Website-$LOCATION_NAME.$DOMAIN_LOCATION.s3-website.us-east-2.amazonaws.com"
DOMAIN_NAME_BUILT="$LOCATION_NAME.$DOMAIN_LOCATION.s3-website.us-east-2.amazonaws.com"
ORIGIN_ID_BUILT="S3-Website-$LOCATION_NAME.$DOMAIN_LOCATION.s3-website.us-east-2.amazonaws.com"
echo "$TARGET_ID_BUILT"
echo "$DOMAIN_NAME_BUILT"
echo "$ORIGIN_ID_BUILT"
How do I write these variables to the json file with jq?
EDIT: Sample of distconfig.json requested – domain/creds swapped to example
{
"CallerReference": "my-test-distribution-2",
"Comment": "",
"CacheBehaviors": {
"Quantity": 0
},
"IsIPV6Enabled": true,
"Logging": {
"Bucket": "",
"Prefix": "",
"Enabled": false,
"IncludeCookies": false
},
"WebACLId": "",
"Origins": {
"Items": [
{
"OriginPath": "",
"CustomOriginConfig": {
"OriginSslProtocols": {
"Items": [
"TLSv1",
"TLSv1.1",
"TLSv1.2"
],
"Quantity": 3
},
"OriginProtocolPolicy": "http-only",
"OriginReadTimeout": 30,
"HTTPPort": 80,
"HTTPSPort": 443,
"OriginKeepaliveTimeout": 5
},
"CustomHeaders": {
"Quantity": 0
},
"Id": "S3-Website-stag4.example.io.s3-website.us-east-2.amazonaws.com",
"DomainName": "stag4.example.io.s3-website.us-east-2.amazonaws.com"
}
],
"Quantity": 1
},
}
"DefaultRootObject": "",
"PriceClass": "PriceClass_All",
"Enabled": true,
"DefaultCacheBehavior": {
"TrustedSigners": {
"Enabled": false,
"Quantity": 0
},
"LambdaFunctionAssociations": {
"Quantity": 0
},
"TargetOriginId": "S3-Website-stag4.example.io.s3-website.us-east-2.amazonaws.com",
"ViewerProtocolPolicy": "redirect-to-https",
"ForwardedValues": {
"Headers": {
"Quantity": 0
},
"Cookies": {
"Forward": "none"
},
"QueryStringCacheKeys": {
"Quantity": 0
},
"QueryString": false
},
"MaxTTL": 31536000,
"SmoothStreaming": false,
"DefaultTTL": 86400,
"AllowedMethods": {
"Items": [
"HEAD",
"GET"
],
"CachedMethods": {
"Items": [
"HEAD",
"GET"
],
"Quantity": 2
},
"Quantity": 2
},
"MinTTL": 0,
"Compress": true
},
"ViewerCertificate": {
"SSLSupportMethod": "sni-only",
"ACMCertificateArn": "xxxx",
"MinimumProtocolVersion": "TLSv1.1_2016",
"Certificate": "xxxx",
"CertificateSource": "acm"
},
"CustomErrorResponses": {
"Quantity": 0
},
"HttpVersion": "http2",
"Restrictions": {
"GeoRestriction": {
"RestrictionType": "none",
"Quantity": 0
}
},
"Aliases": {
"Quantity": 0
}
}

You should use sed to do the substitution and then inject the value back into the JSON.
echo $TARGET_ID | sed 's/stag4/stag5/g'
Outputs
S3-Website-stag5.example.io.s3-website.us-east-2.amazonaws.com
Next we'll put the value back into the original JSON, this will technically output a new JSON and does not edit the file, however, you can easily solve for this on the output by temporarily saving to a tmp file.
We will use the --arg flag to reference our bash variable and set the new value for our field
cat distconfig.json | jq --arg  TARGET_ID $TARGET_ID '.DefaultCacheBehavior.TargetOriginId = $TARGET_ID' > tmp.json && mv tmp.json distconfig.json

Related

replace specific keys in a deeply embedded json file

I have a json file (origin.json) generated locally, I'd like to replace some keys in this origin.json and generate a remote.json so that I could send it to a remote server following it's endpoint payload format.
My origin.json is large and deeply embedded , I could iterate each keys and replace those I need to. But I am wondering is there an efficient and fancy tool could do the same ? Something like jq ?
Below are my embedded json
{
"timeoutMs": 3000,
"requestTopic": "local-cron",
"searchQuery": {
"checkin": "2023-01-10",
"checkout": "2023-01-11",
"numberRoomsNeeded": 0,
"adultsTotal": 2,
"childrenTotal": 0,
"currency": "EUR"
},
"requestContext": {
"userId": 666666666,
"userAuthLevel": 2,
"isUserstar": true,
"visitorCc1": "cn",
"trafficSourceId": 0,
"siteTypeId": 9,
"detectedUserType": "normal",
"travelPurpose": 2,
"affiliateId": 12345,
"languageCode": "en-us",
"currency": "CNY",
"siteType": 1,
"serverRole": "cron",
"action": "bp",
"visitorIdentifier": [
{
"type": "id-single",
"uvi": "00000000000000000000000"
},
{
"type": "user-identity",
"uvi": "66666666"
},
{
"type": "user",
"uvi": "77777777777"
}
],
"isInternal": true,
"enableExperiments": true,
"shouldTrackRequestInExperiments": true,
"starSettings": {
"isUserstar": true,
"isUserstarControlGroup": false,
"canStarUserSeeFreeBreakfast": true,
"canStarUserSeeFreeRoomUpgrade": true,
"starTier": 5,
"topstarBenefit": "",
"isRightstar": true,
"starDynamicPricing": {
"canSeestarDynamicPricingLevel3": true
},
"canStarUserSeeFreeCleaningFee": true,
"starVipSettings": [
{
"eligible": true,
"benefitName": "no_et",
"programType": "PriceMatchTrial",
"percentage": 0
}
]
},
"isCsRelocationMode": false,
"tripValueContext": {},
"visitorCountryRegion": "sh",
"paymentTiming": 1,
"includeConditional": false
},
"showDebug": false,
"hits": [
{
"hhhhid": 8228082,
"ppblock": {
"allMatchingBlocks": [
{
"blockId": 1,
"rawBlock": {
"occupancy": 2,
"price": 34425,
"roomId": 822808201,
"policygroupId": 346547507,
"mealplan": 2,
"channel": 581,
"currencyId": 2,
"maxPersons": 3,
"flags": 0,
"freeCancelUntil": 0,
"priceBase10Exponent": -2,
"packageId": 0,
"paymenttermsId": 38,
"vrFlags": 0,
"bundleId": 0
},
"blockStay": {
"stayNights": [
{
"polId": 346547507,
"rateId": 25728208,
"curId": 2,
"price": 344.25,
"price1": 0,
"channelId": 581,
"occupancy": 2,
"roomId": 822808201,
"initialPrice": 405,
"initialPrice1": 0
}
],
"stayNrRooms": 1,
"stayAvailableUntil": 1956105,
"stayPrice": 344.25,
"stayFlashDeal": 0,
"stayPromoTextId": 0,
"stayMinAdvanceRes": 1673388000,
"stayInventorySegmentId": 0,
"stayExperimentFlags": 0,
"stayRoomRateFlags": 4,
"stayIncludedProducts": 0
}
}
]
},
"selectedBlocks": [
"822808201_346547507_2_2_0"
],
"selected": {
"822808201_346547507_2_2_0": 1
}
}
],
"pipeline": 3
}
Here I flagged severval keys I'd like to replace with '==> (new key)'
jq '.. | keys?' star-dragongate.json
[
"hits",
"pipeline",
"requestContext",
"requestTopic",
"searchQuery",
"showDebug", ==> showdebug
"timeoutMs"
]
[
"adultsTotal",
"checkin",
"checkout",
"childrenTotal",
"currency",
"numberRoomsNeeded"
]
[
"action",
"affiliateId", ==> Affilateid
"currency",
"detectedUserType",
"enableExperiments",
"starSettings",
"includeConditional",
"isCsRelocationMode",
"isInternal",
"isUserstar",
"languageCode",
"paymentTiming",
"serverRole",
"shouldTrackRequestInExperiments", ==> inexperiments
"siteType",
"siteTypeId",
"trafficSourceId",
"travelPurpose",
"tripValueContext",
"userAuthLevel",
"userId",
"visitorCc1",
"visitorCountryRegion",
"visitorIdentifier"
]
[
0,
1,
2
]
[
"type",
"uvi"
]
[
"type",
"uvi"
]
[
"type",
"uvi"
]
[
"canStarUserSeeFreeBreakfast",
"canStarUserSeeFreeCleaningFee",
"canStarUserSeeFreeRoomUpgrade", ==> freeroom_upgrade
"starDynamicPricing",
"starTier",
"starVipSettings",
"isRightstar",
"isUserstar",
"isUserstarControlGroup",
"topstarBenefit"
]
[
"canSeestarDynamicPricingLevel3"
]
[
0
]
[
"benefitName",
"eligible",
"percentage",
"programType"
]
[]
[
0
]
[
"hhhhid",
"ppblock",
"selected",
"selectedBlocks"
]
[
"allMatchingBlocks"
]
[
0
]
[
"blockId",
"blockStay",
"rawBlock"
]
[
"bundleId", ==> bundle_id
"channel",
"currencyId",
"flags",
"freeCancelUntil",
"maxPersons",
"mealplan",
"occupancy",
"packageId",
"paymenttermsId",
"policygroupId",
"price",
"priceBase10Exponent",
"roomId",
"vrFlags"
]
[
"stayAvailableUntil",
"stayExperimentFlags",
"stayFlashDeal",
"stayIncludedProducts",
"stayInventorySegmentId",
"stayMinAdvanceRes",
"stayNights",
"stayNrRooms",
"stayPrice",
"stayPromoTextId", ==> staypromotextid
"stayRoomRateFlags"
]
[
0
]
[
"channelId",
"curId",
"initialPrice",
"initialPrice1",
"occupancy",
"polId",
"price",
"price1",
"rateId",
"roomId"
]
[
0
]
[
"822808201_346547507_2_2_0"
]
The keys I need to replace located in different embed layer and blocks of this json.
Is there any suggestion on how to replace those keys in an efficient way ?
PS: The keys in json file are in static location, not dymanically change all the time.
To rename a field name, you could use with_entries, which gives you access to each .key. Reset it by assignment.
Now, what's still unclear is how you want to find the keys in question (programmatically). If their locations are static, and you know them, address them individually, as in:
.requestContext.starSettings |= with_entries((.key | select(. == "canStarUserSeeFreeRoomUpgrade")) = "freeroom_upgrade")
| .hits[].ppblock.allMatchingBlocks[].rawBlock |= with_entries((.key | select(. == "bundleId")) = "bundle_id")
# and so on...
To reduce redundant code, you could also move the renaming portion of it into its own function, and then just call that, e.g.:
def rename($old; $new):
with_entries((.key | select(. == $old)) = $new);
.requestContext.starSettings |= rename("canStarUserSeeFreeRoomUpgrade"; "freeroom_upgrade")
| .hits[].ppblock.allMatchingBlocks[].rawBlock |= rename("bundleId"; "bundle_id")
# and so on...
Or move the location also into the function, e.g.:
def rename_at(path; $old; $new):
path |= with_entries((.key | select(. == $old)) = $new);
rename_at(.requestContext.starSettings; "canStarUserSeeFreeRoomUpgrade"; "freeroom_upgrade")
| rename_at(.hits[].ppblock.allMatchingBlocks[].rawBlock; "bundleId"; "bundle_id")
# and so on...
If their location is unknown, and you want to replace them just based on their (local) name, you need to traverse the document, and check if you hit a matching name. The walk function provides you with the traversal, objects reduces the action to objects:
walk(objects |= with_entries(
if .key == "canStarUserSeeFreeRoomUpgrade" then .key = "freeroom_upgrade"
elif .key == "bundleId" then .key = "bundle_id"
# and so on...
else . end
))

Using jq to fetch and show key value with quotes

I have a file that looks as below:
{
"Job": {
"Name": "sample_job",
"Description": "",
"Role": "arn:aws:iam::00000000000:role/sample_role",
"CreatedOn": "2021-10-21T23:35:23.660000-03:00",
"LastModifiedOn": "2021-10-21T23:45:41.771000-03:00",
"ExecutionProperty": {
"MaxConcurrentRuns": 1
},
"Command": {
"Name": "glueetl",
"ScriptLocation": "s3://aws-sample-s3/scripts/sample.py",
"PythonVersion": "3"
},
"DefaultArguments": {
"--TempDir": "s3://aws-sample-s3/temporary/",
"--class": "GlueApp",
"--enable-continuous-cloudwatch-log": "true",
"--enable-glue-datacatalog": "true",
"--enable-metrics": "true",
"--enable-spark-ui": "true",
"--job-bookmark-option": "job-bookmark-enable",
"--job-insights-byo-rules": "",
"--job-language": "python",
"--spark-event-logs-path": "s3://aws-sample-s3/logs"
},
"MaxRetries": 0,
"AllocatedCapacity": 100,
"Timeout": 2880,
"MaxCapacity": 100.0,
"WorkerType": "G.1X",
"NumberOfWorkers": 100,
"GlueVersion": "2.0"
}
}
I want to get key/value from "Name", "--enable-continuous-cloudwatch-log": "" and "--enable-metrics": "". So, I need to show the info like this:
"Name" "sample_job"
"--enable-continuous-cloudwatch-log" ""
"--enable-metrics" ""
UPDATE
Follow the tips from #Inian and #0stone0 I came close to it:
jq -r '(.Job ) + (.Job.DefaultArguments | { "--enable-continuous-cloudwatch-log", "--enable-metrics"}) | to_entries[] | "\"\(.key)\" \"\(.value)\""'
This extract the values I need but show all another key/values.
Since you're JSON isn't valid, I've converted it into:
{
"Job": {
"Name": "sample_job",
"Role": "sample_role_job"
},
"DefaultArguments": {
"--enable-continuous-cloudwatch-log": "test_1",
"--enable-metrics": ""
},
"Timeout": 2880,
"NumberOfWorkers": 10
}
Using the following filter:
"Name \(.Job.Name)\n--enable-continuous-cloudwatch-log \(.DefaultArguments."--enable-continuous-cloudwatch-log")\n--enable-metrics \(.DefaultArguments."--enable-metrics")"
We use string interpolation to show the desired output:
Name sample_job
--enable-continuous-cloudwatch-log test_1
--enable-metrics
jq --raw-output '"Name \(.Job.Name)\n--enable-continuous-cloudwatch-log \(.DefaultArguments."--enable-continuous-cloudwatch-log")\n--enable-metrics \(.DefaultArguments."--enable-metrics")"'
Online Demo

Change subelement with jq

I have a structure that looks like so
[
[
{
"ID": "grp1-001",
},
{
"ID": "grp1-002",
},
{
"ID": "grp1-003",
},
{
"ID": "grp1-004",
},
{
"ID": "grp1-005",
},
{
"ID": "grp1-006",
}
],
[
{
"ID": "grp2-001",
},
{
"ID": "grp2-002",
},
{
"ID": "grp2-003",
},
{
"ID": "grp2-004",
},
{
"ID": "grp2-005",
},
{
"ID": "grp2-006",
}
.......
what I need to get as a result of the modification is this
[
[
["1", "grp1-001"],
["2", "grp1-002"],
["3", "grp1-003"],
["4", "grp1-004"],
["5", "grp1-005"],
["6", "grp1-006"],
],
[
["1", "grp2-001"],
["2", "grp2-002"],
["3", "grp2-003"],
["4", "grp2-004"],
["5", "grp2-005"],
["6", "grp2-006"],
],
Which means I need to keep the external structure (outside array and an internal grouping) but convert the inner dict to an array and replace the "ID" key with a value (that will come from external source like --argjson). I am not even sure how to start - any ideas/resources are highly appreciated.
Assuming you're just taking the objects and transforming them to pairs of the index in the array and the ID value, you could do this:
map([to_entries[] | [.key + 1, .value.ID | tostring]])
https://jqplay.org/s/RBac7SPfdG
Using to_entries/0 on an array gives you an array of key/value (index/value) pairs. You could then shift the indices by 1 and convert to strings.

How to parse this file with jq?

I just started using jq and json files, and I'm trying to parse a specific file.
I'm tring to do it with jq in command line, but if there's any other way to do it properly, I'm in to give it a try.
The file itself looks like this :
{
"Status": "ok",
"Code": 200,
"Message": "",
"Result": [
{
"ID": 123456,
"Activity": 27,
"Name": Example1",
"Coordinate": {
"Galaxy": 1,
"System": 22,
"Position": 3
},
"Administrator": false,
"Inactive": false,
"Vacation": false,
"HonorableTarget": false,
"Debris": {
"Metal": 0,
"Crystal": 0,
"RecyclersNeeded": 0
},
"Moon": null,
"Player": {
"ID": 111111,
"Name": "foo",
"Rank": 4
},
"Alliance": null
},
{
"ID": 223344,
"Activity": 17,
"Name": "Example2",
"Coordinate": {
"Galaxy": 3,
"System": 44,
"Position": 5
},
"Administrator": false,
"Inactive": false,
"Vacation": false,
"StrongPlayer": false,
"HonorableTarget": false,
"Debris": {
"Metal": 0,
"Crystal": 0,
"RecyclersNeeded": 0
},
"Moon": null,
"Player": {
"ID": 765432,
"Name": "Player 2",
"Rank": 3
},
"Alliance": null
},
(...)
]
}
I would need to extract information based on the galaxy/system/position.
For example, having a script with the proper filters in it and execute something like that :
./parser --galaxy=1 --system=22 --position=3
And it would give me :
ID : 123456
Name : Example1
Activity : 27
...
I tried to do that with curl to grab my json file and jq to parse my file, but I have no idea how I can make that kind of request.
The following should be sufficient to get you on your way.
First, let's assume the JSON is in a file name galaxy.json; second, let's assume the file galaxy.jq contains the following:
.Result[]
| select(.Coordinate | (.Galaxy==$galaxy and .System==$system and .Position==$position))
Then the invocation:
jq -f so-galaxy.jq --argjson galaxy 1 --argjson system 22 --argjson position 3 galaxy.json
would yield the corresponding object:
{
"ID": 123456,
"Activity": 27,
"Name": "Example1",
"Coordinate": {
"Galaxy": 1,
"System": 22,
"Position": 3
},
"Administrator": false,
"Inactive": false,
"Vacation": false,
"HonorableTarget": false,
"Debris": {
"Metal": 0,
"Crystal": 0,
"RecyclersNeeded": 0
},
"Moon": null,
"Player": {
"ID": 111111,
"Name": "foo",
"Rank": 4
},
"Alliance": null
}
Key: Value format
If you want the output to be in key: value format, simply add -r to the command-line options, and append the following to the jq filter:
| to_entries[]
| "\(.key): \(.value)"
Output
ID: 123456
Activity: 27
Name: Example1
Coordinate: {"Galaxy":1,"System":22,"Position":3}
Administrator: false
Inactive: false
Vacation: false
HonorableTarget: false
Debris: {"Metal":0,"Crystal":0,"RecyclersNeeded":0}
Moon: null
Player: {"ID":111111,"Name":"foo","Rank":4}
Alliance: null

How to add tag inside a nest depending on a variable while keeping rest of json

I'm failry new to jq and I've learned to do most things on my own but I'm hitting my head on my keyboard for this one. Look at the following json
JSON:
{
"importType": "Upsert",
"immediateDeployment": false,
"isIgnoreNulls": false,
"isOverwriteNullsOnly": false,
"isPreferredandSync": false,
"outputLayout": {
"fields": [
{
"name": "TEMP_KEY",
"type": "String",
"length": "2000",
"displayName": "TEMP_KEY"
},
{
"name": "LoadSeqNum",
"type": "String",
"length": "2000",
"displayName": "LoadSeqNum"
}
]
}
}
What I'm trying to do is inside of .outputLayout.fields[] I want to create a new pair called "isIdentifier" where it is true if the .outputLayout.fields[].name is LoadSeqNum and false if it's not but I need to keep the rest of the json just as it is. So target should look as following:
Goal:
{
"importType": "Upsert",
"immediateDeployment": false,
"isIgnoreNulls": false,
"isOverwriteNullsOnly": false,
"isPreferredandSync": false,
"outputLayout": {
"fields": [
{
"name": "TEMP_KEY",
"type": "String",
"length": "2000",
"displayName": "TEMP_KEY"
"isIdentifier": false
},
{
"name": "LoadSeqNum",
"type": "String",
"length": "2000",
"displayName": "LoadSeqNum"
"isIdentifier": true
}
]
}
}
I tried this:
jq '.outputLayout.fields[] | . + {"isIdentifier": (if (.name)=="LoadSeqNum" then true else false end)}'
But of course I'm missing all the higher level things. When I try to do:
.outputLayout.fields[].isIdentifier=(if (.outputLayout.fields[].name)=="LoadSeqNum" then true else false end)
I get the whole thing twice, once with both true and the other one with both false. I understand why it's doing that but I'm having a tough time figuring out what would work. Any help or point in right direction?
.outputLayout.fields[] |= (.isIdentifier = (.displayName == "LoadSeqNum") )
Or equivalently but perhaps a little less cryptically:
.outputLayout.fields |= map( .isIdentifier = (.displayName == "LoadSeqNum") )