I'm trying to extract info on certain specific dependencies from the output of DTrack's /v1/project
endpoint.
The API returns data in the format:
[{
"name": "project name",
"classifier": "LIBRARY | APPLICATION",
"directDependencies": <escaped JSON string in which I need to search for "com.example.commons/dep1@0.101.0" and similar strings>
}, ...]
Here's what I got so far:
jq '.[] | select(.classifier=="APPLICATION") | {
id: .name,
dep1: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep1@(?<version>[0-9.]+)"])),
dep2: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep2@(?<version>[0-9.]+)"])),
dep3: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep3@(?<version>[0-9.]+)"])),
dep4: ((.directDependencies // "") | capture(["com\\.example\\.commons/dep4@(?<version>[0-9.]+)"]))
}'
Generated output looks fine:
{
"id": "project1",
"dep1": {
"version": "0.102.0"
},
"dep2": {
"version": "0.102.0"
},
"dep3": {
"version": "0.102.0"
},
"dep4": {
"version": "0.102.0"
}
}
... // more elements
The problem is, the output only contains those of the input elements for which all four capture
s matched something.
I would like the output to contain all the elements of the input list, even if some of the capture
s fail to match. In other words, I would expect elements in the output like:
{
"id": "project1",
"dep1": { "version": "0.1.0" },
// dep2 didn't match
// dep3 didn't match
"dep4": { "version": "0.3.0" }
}
I couldn't find such an option in the docs. Any ideas how to solve this?
As I understand the question, you want to only add successful matches to the result object. I would prefer using scan
to find all matches, and then reduce
over them to successively build up the result object, while including the dependency name in the regex (e.g. as dep1|dep2|dep3|dep4
, or shorter dep[1-4]
, or more generically [^@]+
, which I've used below).
.[] | select(.classifier/" | " | any(. == "APPLICATION")) | reduce (
.directDependencies | scan("com\\.example\\.commons/(?<dep>[^@]+)@(?<version>[0-9.]+)")
) as [$dep, $version] ({id: .name}; .[$dep] = {$version})
Note #1: This will always generate the {id: .name}
part, even if no dependencies are added. To instead swallow these "naked" objects, start out the reduction with null
, and filter eventually for non-null values
:
.[] | select(.classifier/" | " | any(. == "APPLICATION")) | {id: .name} + (reduce (
.directDependencies | scan("com\\.example\\.commons/(?<dep>[^@]+)@(?<version>[0-9.]+)")
) as [$dep, $version] (null; .[$dep] = {$version}) | values)
Note #2: Your filter select(.classifier=="APPLICATION")
only matches objects where the value under .classifier
is completely equal to the string "APPLICATION"
, and thus wouldn't catch your sample input at all as it contains {"classifier": "LIBRARY | APPLICATION"}
. So instead, I used /
to split that value at the substring " | "
, and had it match if any
(i.e. at least one) of the resulting parts was equal to that string.