删除所有空值
Remove all null values
我正在尝试使用 jq 从 json 对象中删除空值。我在他们的 github 上找到了 this issue,所以现在我试图用 del
删除它们。
我有这个:
'{ id: $customerId, name, phones: ([{ original: .phone },
{ original: .otherPhone}]), email} | del(. | nulls)'
这似乎没有任何作用。但是,如果我将 nulls
替换为 .phones
,它会删除 phone 数字。
下面说明了如何从 JSON 对象中删除所有空值键:
jq -n '{"a":1, "b": null, "c": null} | with_entries( select( .value != null ) )'
{
"a": 1
}
或者,paths/0
可以按如下方式使用:
. as $o | [paths[] | {(.) : ($o[.])} ] | add
顺便说一下,del/1
也可以用来达到相同的结果,例如使用此过滤器:
reduce keys[] as $k (.; if .[$k] == null then del(.[$k]) else . end)
或不太明显,但更简洁:
del( .[ (keys - [paths[]])[] ] )
郑重声明,这里有两种使用方法 delpaths/1
:
jq -n '{"a":1, "b": null, "c": null, "d":2} as $o
| $o
| delpaths( [ keys[] | select( $o[.] == null ) ] | map( [.]) )'
$ jq -n '{"a":1, "b": null, "c": null, "d":2}
| [delpaths((keys - paths) | map([.])) ] | add'
在后两种情况下,输出是相同的:
{
"a": 1,
"d": 2
}
这不是 del/1
的用途。给定一个对象作为输入,如果你想删除 .phones
属性,你会做:
del(.phones)
换句话说,del
的参数是您要删除的 属性 的路径。
如果你想使用它,你必须找出 null
值的所有路径并将其传递给它。不过那会比较麻烦。
流式输入可以使这项任务更加简单。
fromstream(tostream | select(length == 1 or .[1] != null))
否则,对于更直接的方法,您将不得不遍历对象树以找到 null
值。如果找到,将其过滤掉。使用 walk/1
,您的过滤器可以递归应用以排除 null
值。
walk(
(objects | with_entries(select(.value != null)))
// (arrays | map(select(. != null)))
// values
)
因此,如果您有此输入:
{
"foo": null,
"bar": "bar",
"biz": [1,2,3,4,null],
"baz": {
"a": 1,
"b": null,
"c": ["a","b","c","null",32,null]
}
}
此过滤器将产生:
{
"bar": "bar",
"baz": {
"a": 1,
"c": ["a","b","c","null",32]
},
"biz": [1,2,3,4]
}
[警告:此回复中给出的 walk/1
的定义是有问题的,尤其是第一条评论中给出的原因;另请注意,jq 1.6 对 walk/1
的定义不同。]
我正在添加新答案以强调@jeff-mercado 脚本的扩展版本。我的脚本版本假定空值如下:
null
;
[]
- 空数组;
{}
- 空对象。
删除空数组和对象是从这里借来的。
def walk(f):
. as $in |
if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ( $in[$key] | walk(f) ) } ) | f
elif type == "array" then
select(length > 0) | map( walk(f) ) | f
else
f
end;
walk(
if type == "object" then
with_entries(select( .value != null and .value != {} and .value != [] ))
elif type == "array" then
map(select( . != null and . != {} and .!= [] ))
else
.
end
)
[警告:下面的响应有几个问题,尤其是那些因 0|length
为 0 而引起的问题。]
详细说明 earlier answer,除了删除具有 null
值的属性外,删除具有空数组或对象值的 JSON 属性(即 []
或 {}
),也是。
ℹ️ — jq's walk()
function (walk/1
) makes this easy. walk()
will be
available in a future version of jq (> jq-1.5
), but its
definition
can be added to current filters.
要传递给 walk()
以删除空值和空结构的条件是:
walk(
if type == "object" then with_entries(select(.value|length > 0))
elif type == "array" then map(select(length > 0))
else .
end
)
鉴于此 JSON 输入:
{
"notNullA": "notNullA",
"nullA": null,
"objectA": {
"notNullB": "notNullB",
"nullB": null,
"objectB": {
"notNullC": "notNullC",
"nullC": null
},
"emptyObjectB": {},
"arrayB": [
"b"
],
"emptyBrrayB": []
},
"emptyObjectA": {},
"arrayA": [
"a"
],
"emptyArrayA": []
}
使用这个函数得到结果:
{
"notNullA": "notNullA",
"objectA": {
"notNullB": "notNullB",
"objectB": {
"notNullC": "notNullC"
},
"arrayB": [
"b"
]
},
"arrayA": [
"a"
]
}
在此页面的其他地方,有人表达了一些兴趣
使用 jq 消除递归出现的 [] 和 {} 以及 null.
尽管可以使用 walk/1
的内置定义来做
这个,正确地做到这一点有点棘手。因此这是一个变体版本
walk/1
这使得这样做变得微不足道:
def traverse(f):
if type == "object" then map_values(traverse(f)) | f
elif type == "array" then map( traverse(f) ) | f
else f
end;
为了方便修改删除元素的条件,
我们定义:
def isempty: .==null or ((type|(.=="array" or .=="object")) and length==0);
现在的解决方案很简单:
traverse(select(isempty|not))
到目前为止,这里的所有其他答案都是旧版本 jq
的解决方法,目前尚不清楚如何在最新发布的版本中简单地做到这一点。在 JQ 1.6 或更新版本中,这将完成递归删除空值的工作:
$ jq 'walk( if type == "object" then with_entries(select(.value != null)) else . end)' input.json
来自 this comment 关于上游讨论添加 walk()
功能的问题。
使用较新版本的 jq(1.6 及更高版本)
您可以使用此表达式以递归方式删除空值键:
jq 'walk( if type == "object" then with_entries(select(.value != null)) else . end)'
This answer by Michael Homer on https://unix.stackexchange.com 有一个超级简洁的解决方案,自 jq 1.6 起有效:
del(..|nulls)
它会从您的 JSON 中删除所有空值属性(和值)。简单又甜蜜:)
nulls
是内置过滤器,可以用自定义选择替换:
del(..|select(. == "value to delete"))
根据多个条件删除元素,例如删除所有布尔值和所有数字:
del(..|booleans,numbers)
或者,只删除不符合条件的节点:
del(..|select(. == "value to keep" | not))
(最后一个例子只是说明性的——当然你可以将 ==
换成 !=
,但有时这是不可能的。例如 keep所有真值:del(..|select(.|not))
)
我正在尝试使用 jq 从 json 对象中删除空值。我在他们的 github 上找到了 this issue,所以现在我试图用 del
删除它们。
我有这个:
'{ id: $customerId, name, phones: ([{ original: .phone },
{ original: .otherPhone}]), email} | del(. | nulls)'
这似乎没有任何作用。但是,如果我将 nulls
替换为 .phones
,它会删除 phone 数字。
下面说明了如何从 JSON 对象中删除所有空值键:
jq -n '{"a":1, "b": null, "c": null} | with_entries( select( .value != null ) )'
{
"a": 1
}
或者,paths/0
可以按如下方式使用:
. as $o | [paths[] | {(.) : ($o[.])} ] | add
顺便说一下,del/1
也可以用来达到相同的结果,例如使用此过滤器:
reduce keys[] as $k (.; if .[$k] == null then del(.[$k]) else . end)
或不太明显,但更简洁:
del( .[ (keys - [paths[]])[] ] )
郑重声明,这里有两种使用方法 delpaths/1
:
jq -n '{"a":1, "b": null, "c": null, "d":2} as $o
| $o
| delpaths( [ keys[] | select( $o[.] == null ) ] | map( [.]) )'
$ jq -n '{"a":1, "b": null, "c": null, "d":2}
| [delpaths((keys - paths) | map([.])) ] | add'
在后两种情况下,输出是相同的: { "a": 1, "d": 2 }
这不是 del/1
的用途。给定一个对象作为输入,如果你想删除 .phones
属性,你会做:
del(.phones)
换句话说,del
的参数是您要删除的 属性 的路径。
如果你想使用它,你必须找出 null
值的所有路径并将其传递给它。不过那会比较麻烦。
流式输入可以使这项任务更加简单。
fromstream(tostream | select(length == 1 or .[1] != null))
否则,对于更直接的方法,您将不得不遍历对象树以找到 null
值。如果找到,将其过滤掉。使用 walk/1
,您的过滤器可以递归应用以排除 null
值。
walk(
(objects | with_entries(select(.value != null)))
// (arrays | map(select(. != null)))
// values
)
因此,如果您有此输入:
{
"foo": null,
"bar": "bar",
"biz": [1,2,3,4,null],
"baz": {
"a": 1,
"b": null,
"c": ["a","b","c","null",32,null]
}
}
此过滤器将产生:
{
"bar": "bar",
"baz": {
"a": 1,
"c": ["a","b","c","null",32]
},
"biz": [1,2,3,4]
}
[警告:此回复中给出的 walk/1
的定义是有问题的,尤其是第一条评论中给出的原因;另请注意,jq 1.6 对 walk/1
的定义不同。]
我正在添加新答案以强调@jeff-mercado 脚本的扩展版本。我的脚本版本假定空值如下:
null
;[]
- 空数组;{}
- 空对象。
删除空数组和对象是从这里借来的。
def walk(f):
. as $in |
if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ( $in[$key] | walk(f) ) } ) | f
elif type == "array" then
select(length > 0) | map( walk(f) ) | f
else
f
end;
walk(
if type == "object" then
with_entries(select( .value != null and .value != {} and .value != [] ))
elif type == "array" then
map(select( . != null and . != {} and .!= [] ))
else
.
end
)
[警告:下面的响应有几个问题,尤其是那些因 0|length
为 0 而引起的问题。]
详细说明 earlier answer,除了删除具有 null
值的属性外,删除具有空数组或对象值的 JSON 属性(即 []
或 {}
),也是。
ℹ️ — jq's
walk()
function (walk/1
) makes this easy.walk()
will be available in a future version of jq (>jq-1.5
), but its definition can be added to current filters.
要传递给 walk()
以删除空值和空结构的条件是:
walk(
if type == "object" then with_entries(select(.value|length > 0))
elif type == "array" then map(select(length > 0))
else .
end
)
鉴于此 JSON 输入:
{
"notNullA": "notNullA",
"nullA": null,
"objectA": {
"notNullB": "notNullB",
"nullB": null,
"objectB": {
"notNullC": "notNullC",
"nullC": null
},
"emptyObjectB": {},
"arrayB": [
"b"
],
"emptyBrrayB": []
},
"emptyObjectA": {},
"arrayA": [
"a"
],
"emptyArrayA": []
}
使用这个函数得到结果:
{
"notNullA": "notNullA",
"objectA": {
"notNullB": "notNullB",
"objectB": {
"notNullC": "notNullC"
},
"arrayB": [
"b"
]
},
"arrayA": [
"a"
]
}
在此页面的其他地方,有人表达了一些兴趣 使用 jq 消除递归出现的 [] 和 {} 以及 null.
尽管可以使用 walk/1
的内置定义来做
这个,正确地做到这一点有点棘手。因此这是一个变体版本
walk/1
这使得这样做变得微不足道:
def traverse(f):
if type == "object" then map_values(traverse(f)) | f
elif type == "array" then map( traverse(f) ) | f
else f
end;
为了方便修改删除元素的条件, 我们定义:
def isempty: .==null or ((type|(.=="array" or .=="object")) and length==0);
现在的解决方案很简单:
traverse(select(isempty|not))
到目前为止,这里的所有其他答案都是旧版本 jq
的解决方法,目前尚不清楚如何在最新发布的版本中简单地做到这一点。在 JQ 1.6 或更新版本中,这将完成递归删除空值的工作:
$ jq 'walk( if type == "object" then with_entries(select(.value != null)) else . end)' input.json
来自 this comment 关于上游讨论添加 walk()
功能的问题。
使用较新版本的 jq(1.6 及更高版本)
您可以使用此表达式以递归方式删除空值键:
jq 'walk( if type == "object" then with_entries(select(.value != null)) else . end)'
This answer by Michael Homer on https://unix.stackexchange.com 有一个超级简洁的解决方案,自 jq 1.6 起有效:
del(..|nulls)
它会从您的 JSON 中删除所有空值属性(和值)。简单又甜蜜:)
nulls
是内置过滤器,可以用自定义选择替换:
del(..|select(. == "value to delete"))
根据多个条件删除元素,例如删除所有布尔值和所有数字:
del(..|booleans,numbers)
或者,只删除不符合条件的节点:
del(..|select(. == "value to keep" | not))
(最后一个例子只是说明性的——当然你可以将 ==
换成 !=
,但有时这是不可能的。例如 keep所有真值:del(..|select(.|not))
)