Reputation: 74615
Supposing that I have a JSON file that looks like this:
{
"name": "tom",
"scripts": {
"webpack": "webpack --progress --colors --watch"
},
"dependencies": {
},
"devDependencies": {
"webpack": "^2.2.1"
}
}
I would like to be able to specify a package by name, find the corresponding entry in either dependencies
or devDependencies
and update the value.
The closest I got was this:
$ jq --arg p webpack --arg v 1.2.3 'to_entries | map(
if (.value[$p]? | startswith("^")?) then
.value[$p] = $v
else .
end
) | from_entries' file.json
Which updates the value but also removes the dependencies
and the name
property:
{
"scripts": {
"webpack": "webpack --progress --colors --watch"
},
"devDependencies": {
"webpack": "1.2.3"
}
}
How can I update the desired value without affecting the other properties in the original JSON?
Upvotes: 2
Views: 1215
Reputation: 116740
Using your approach, it would be better to use map_values:
map_values(if type == "object"
then with_entries( if .key == $p and (.value | startswith("^")?)
then .value = $v else . end )
else . end)
You might also like to consider using walk/1
:
walk(if type == "object" and has($p) and (.[$p]|startswith("^"))
then .[$p] = $v else . end)
The solution using walk/1
will examine the JSON entity recursively. If your jq does not have walk
, its definition in jq can readily be found by googling.
def when(p;q): if p//false then q else . end;
map_values( when(type == "object";
with_entries( when( .key == $p and (.value | startswith("^")?);
.value = $v) )) )
Upvotes: 1
Reputation: 134841
Try this instead:
$ jq --arg p webpack --arg v 1.2.3 '
def update_package($package; $version):
if has($package) then .[$package] = $version
else . end;
.dependencies |= update_package($p; $v)
| .devDependencies |= update_package($p; $v)' project.json
update_package/2
will only update the dependency object if it actually references the given project, otherwise it is left alone. Apply to both dependencies
and devDependencies
.
The problem with your original filter that was removing the dependencies
object was that since the project didn't exist, there was no corresponding value to update. By applying startswith
on the nonexistent value, it caused an error. That error in turn was just ignored and effectively skipped the corresponding item in the map, thus losing that property.
For a quick fix to ensure this doesn't happen, don't ignore the error, provide an alternate value to use when the property doesn't exist.
.value[$p]? | startswith("^")? # bad
.value[$p] // "" | startswith("^") # better
Upvotes: 1