jq - 检查子键而不过滤
jq - check for subkey without filtering
我想在不过滤数据的情况下使用 jq 检查 JSON 文件中是否存在子项(或其类型)。我需要它来从以下 JSON 文件中获取所有实体的列表,如果有不止一对可用,则使用第一对坐标。问题在于部分嵌套结构:“位置”对象仅适用于某些条目,“坐标”元素仅在多个位置可用时才是数组。
{"records":[
{
"id": 1,
"location": {
"coordinates": {
"lat": 42,
"lon": -71
}
}
},
{
"id": 2,
"location": {
"coordinates": [
{
"lat": 40,
"lon": -73
},
{
"lat": 39,
"lon": -75
}
]
}
},
{
"id": 3,
"location": null
}]}
所以我尝试了“has”功能,它似乎对子键不起作用。我想象的是这样的:
cat file.json | jq '.records[] | if has("location.coordinates") then [do something] else [do something else] end'
有什么方法可以检查子项吗?由于我需要维护数据集中的所有条目,因此似乎无法通过“select”等方式进行过滤。
澄清我的问题:我希望得到类似于此的 JSON 输出(但我很乐意处理其他格式):
{"records":[
{"id": 1, "lat", xx, "lon": xx}
{"id": 2, "lat", yy, "lon": yy}
{"id": 3, "lat", null, "lon": null}
]}
您可以使用 select
和 |=
运算符代替 has
:
.records |= map(.id as $id | (.location.coordinates | (if type == "array" then .[0] else . end) as $q | ({ $id, lat: null, lon: null } + $q) ))
这会生成:
{
"records": [
{
"id": 1,
"lat": 42,
"lon": -71
},
{
"id": 2,
"lat": 40,
"lon": -73
},
{
"id": 3,
"lat": null,
"lon": null
}
]
}
正如您可以在此 online demo 中尝试的那样。
所以,以上解释:
- 循环
records
并更新它们
.records |= map()
- 保存 ID
.id as $id
- 坐标继续
.location.coordinates |
- 检查一个数组,如果是,就得到第一个对象,否则,就保留它,另存为
$q
(if type == "array" then .[0] else . end) as $q
- 创建最终对象,从仅将
id
和 lat
和 lon
设置为 null
的对象开始,然后我们合并 $q
从坐标中获取实际值
({ $id, lat: null, lon: null } + $q)
这很可能会被简化,所以等待另一个具有相同想法但更优化的答案。
您还可以使用替代运算符 //
:
jq '.records[] |= {id}+((.location.coordinates? |
if type == "array" then .[0] else . end
) // {}
)' input.json
如果前一个案例失败,您可以使用 error suppression operator ?
in combination with the alterative operator //
回退到另一个案例:
.records[] |= (.location.coordinates | .[0]? // .) as {$lat,$lon} | {id,$lat,$lon}
{
"records": [
{
"id": 1,
"lat": 42,
"lon": -71
},
{
"id": 2,
"lat": 40,
"lon": -73
},
{
"id": 3,
"lat": null,
"lon": null
}
]
}
在 1.6 版中,jq
引入了 destructuring alternative operator ?//
其中
provides a concise mechanism for destructuring an input that can take one of several forms
这样一来,您可以使用相同的变量名定义一种情况下使用数组,另一种情况下不使用数组。由于“不匹配”变量(在成功的替代方案中)设置为 null
,因此无需明确处理第三种情况。
jq '.records |= map(
.location as {coordinates: [{$lat, $lon}]} ?// {coordinates: {$lat, $lon}}
| {id, $lat, $lon}
)'
{
"records": [
{
"id": 1,
"lat": 42,
"lon": -71
},
{
"id": 2,
"lat": 40,
"lon": -73
},
{
"id": 3,
"lat": null,
"lon": null
}
]
}
我想在不过滤数据的情况下使用 jq 检查 JSON 文件中是否存在子项(或其类型)。我需要它来从以下 JSON 文件中获取所有实体的列表,如果有不止一对可用,则使用第一对坐标。问题在于部分嵌套结构:“位置”对象仅适用于某些条目,“坐标”元素仅在多个位置可用时才是数组。
{"records":[
{
"id": 1,
"location": {
"coordinates": {
"lat": 42,
"lon": -71
}
}
},
{
"id": 2,
"location": {
"coordinates": [
{
"lat": 40,
"lon": -73
},
{
"lat": 39,
"lon": -75
}
]
}
},
{
"id": 3,
"location": null
}]}
所以我尝试了“has”功能,它似乎对子键不起作用。我想象的是这样的:
cat file.json | jq '.records[] | if has("location.coordinates") then [do something] else [do something else] end'
有什么方法可以检查子项吗?由于我需要维护数据集中的所有条目,因此似乎无法通过“select”等方式进行过滤。
澄清我的问题:我希望得到类似于此的 JSON 输出(但我很乐意处理其他格式):
{"records":[
{"id": 1, "lat", xx, "lon": xx}
{"id": 2, "lat", yy, "lon": yy}
{"id": 3, "lat", null, "lon": null}
]}
您可以使用 select
和 |=
运算符代替 has
:
.records |= map(.id as $id | (.location.coordinates | (if type == "array" then .[0] else . end) as $q | ({ $id, lat: null, lon: null } + $q) ))
这会生成:
{
"records": [
{
"id": 1,
"lat": 42,
"lon": -71
},
{
"id": 2,
"lat": 40,
"lon": -73
},
{
"id": 3,
"lat": null,
"lon": null
}
]
}
正如您可以在此 online demo 中尝试的那样。
所以,以上解释:
- 循环
records
并更新它们
.records |= map()
- 保存 ID
.id as $id
- 坐标继续
.location.coordinates |
- 检查一个数组,如果是,就得到第一个对象,否则,就保留它,另存为
$q
(if type == "array" then .[0] else . end) as $q
- 创建最终对象,从仅将
id
和lat
和lon
设置为null
的对象开始,然后我们合并$q
从坐标中获取实际值
({ $id, lat: null, lon: null } + $q)
这很可能会被简化,所以等待另一个具有相同想法但更优化的答案。
您还可以使用替代运算符 //
:
jq '.records[] |= {id}+((.location.coordinates? |
if type == "array" then .[0] else . end
) // {}
)' input.json
如果前一个案例失败,您可以使用 error suppression operator ?
in combination with the alterative operator //
回退到另一个案例:
.records[] |= (.location.coordinates | .[0]? // .) as {$lat,$lon} | {id,$lat,$lon}
{
"records": [
{
"id": 1,
"lat": 42,
"lon": -71
},
{
"id": 2,
"lat": 40,
"lon": -73
},
{
"id": 3,
"lat": null,
"lon": null
}
]
}
在 1.6 版中,jq
引入了 destructuring alternative operator ?//
其中
provides a concise mechanism for destructuring an input that can take one of several forms
这样一来,您可以使用相同的变量名定义一种情况下使用数组,另一种情况下不使用数组。由于“不匹配”变量(在成功的替代方案中)设置为 null
,因此无需明确处理第三种情况。
jq '.records |= map(
.location as {coordinates: [{$lat, $lon}]} ?// {coordinates: {$lat, $lon}}
| {id, $lat, $lon}
)'
{
"records": [
{
"id": 1,
"lat": 42,
"lon": -71
},
{
"id": 2,
"lat": 40,
"lon": -73
},
{
"id": 3,
"lat": null,
"lon": null
}
]
}