删除所有空值

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)'

REF

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)))