Reputation: 960
So without try and catch in my jq line everything goes well.
Thing is I need the try and catch because this example works, but not for all input:
> cat movies.json | jq '.[3]' |
jq '.release_date |= if . == null or . == ""
then .
else (. | strptime("%Y-%m-%d") | mktime) end'
{
"id": "166428",
"title": "How to Train Your Dragon: The Hidden World",
"poster": "https://image.tmdb.org/t/p/w1280/xvx4Yhf0DVH8G4LzNISpMfFBDy2.jpg",
"overview": "As Hiccup fulfills his dream of creating a peaceful dragon utopia, Toothless’ discovery of an untamed, elusive mate draws the Night Fury away. When danger mounts at home and Hiccup’s reign as village chief is tested, both dragon and rider must make impossible decisions to save their kind.",
"release_date": 1546473600
}
When I add try and catch I get this:
> cat movies.json | jq '.[3]' |
jq '.release_date |= try (if . == null or . == ""
then .
else (. | strptime("%Y-%m-%d") | mktime) end)
catch (.)'
{
"id": "166428",
"title": "How to Train Your Dragon: The Hidden World",
"poster": "https://image.tmdb.org/t/p/w1280/xvx4Yhf0DVH8G4LzNISpMfFBDy2.jpg",
"overview": "As Hiccup fulfills his dream of creating a peaceful dragon utopia, Toothless’ discovery of an untamed, elusive mate draws the Night Fury away. When danger mounts at home and Hiccup’s reign as village chief is tested, both dragon and rider must make impossible decisions to save their kind.",
"release_date": {
"__jq": 0
}
}
This is my version:
> jq --version
jq-1.6
In the end I want to get something like this working:
> cat movies.json |
jq 'map_values(try (.release_date |= if . == null or . == ""
then .
else (. | strptime("%Y-%m-%d") | mktime) end)
catch (.))'
Upvotes: 1
Views: 4620
Reputation: 116670
In brief, jq 1.6 introduced a bug affecting the handling of catch/try in the context of |=
.
The simple workaround in the present case is to avoid |=
e.g. by:
(.release_date
| try (if . == null or . == ""
then .
else strptime("%Y-%m-%d") | mktime end)
catch .) as $r
| .release_date = $r'
Notice there is no need for an initial . |
in the else
clause.
Upvotes: 2
Reputation: 16105
Update: It appears you discovered a bug.
Some parts of your problem statement are unclear to me:
You special-case null
and ""
, but if .
is otherwise unparseable, you let strptime/1
error and presumably want to return the unparseable input (via catch (.)
, which has a subtlety to it). Why not just try and parse the date and fall back to the unparseable input regardless of special cases?
If you do intend to create a special-case, why not let this be if type == "string"
? This is, after all, the only meaningful type to feed strptime/1
(even if it might not contain a parseable date).
When defaulting to the unparseable input, the output type/schema becomes unpredictable, but perhaps this is okay for you.
The "In the end" part suggests, by inlining .release_date
within try
, that this field may be optional. I don't know if you intended to signify that this may be the case, so I'm choosing to go with the assumption that it's not going to be, since it isn't specified.
Here's a simplified example, some object properties removed:
$ jq -c '.[]' release_date.json
{"id":"42","release_date":"2019-12-31"}
{"id":"42","release_date":null}
{"id":"42","release_date":"SOON!"}
$ jq 'map(.release_date |= . as $date | try (strptime("%Y-%m-%d") | mktime) catch $date)' release_date.json
[
{
"id": "42",
"release_date": 1577750400
},
{
"id": "42",
"release_date": null
},
{
"id": "42",
"release_date": "SOON!"
}
]
In the catch
block, .
refers to the stringified exception, so to default to the unparseable value, it is temporarily referred to as $date
. Or using a user-defined function with a $value
argument:
$ jq 'def parsedate($date):
try ($date | strptime("%Y-%m-%d") | mktime)
catch $date;
map(.release_date |= parsedate(.))' release_date.json
Interestingly, the solutions above don't work in jq 1.6.
I've tried to narrow the discrepancy down to a minimum.
https://jqplay.org/s/M_RpdNHvHF:
$ jq-1.6 '. |= try . catch .' <<< 1
{
"__jq": 0
}
Until this unintended behavior changes, avoiding |=
and try
-catch
together is a viable option:
https://jqplay.org/s/ki8I1YnU56:
$ jq 'map(.release_date = (.release_date | . as $date
| try (strptime("%Y-%m-%d") | mktime) catch $date))' release_date.json
https://jqplay.org/s/i4FJPpXEG0:
$ jq 'def parsedate($date):
try ($date | strptime("%Y-%m-%d") | mktime)
catch $date;
map(.release_date = parsedate(.release_date))' release_date.json
I've reported it here.
Upvotes: 2