How can I exclude all keys with a specific value inside a JSON with jq?

How can I exclude all keys with a specific value inside a JSON with jq?

Let's say I have a JSON like the following:

{
    "key1": {
        "keyA": "1",
        "keyB": "null",
        "keyC": "null"
    },
    "key2": {
        "keyA": "null",
        "keyB": "3",
        "keyC": "null"
    }
}

I'd like to find a way of excluding all keys with the value null on my JSON. So the result would be the following:

{
    "key1": {
        "keyA": "1"
    },
    "key2": {
        "keyB": "3"
    }
}

I know I can exclude specific keys with their names using jq, e.g. cat myjson.json | jq 'del(.[]|.keyA)', this will erase all my keyA keys inside the json, but I want to exclude the keys according to their values... How can I exclude all keys with value "null" using jq?

Antwort1

del(..|select(. == "null"))

This uses the recursive-descent operator .. and select function to find all the locations anywhere in the object with values that are equal to "null" and gives them to del. select evaluates a boolean expression to decide whether to include a particular value or not, while .. gives every single value in the tree to it, and del accepts any expression that produces locations, which this does. (demo)

You can use the path function to check what it's finding:

path(..|select(. == "null"))

and debug what it thinks you're trying to delete first. The output is an array of keys to follow recursively to reach the value, and so the final item in the array is the key that would actually be deleted.

You can also use update-assignment with |= empty in jq 1.6 and up (it silently fails in earlier versions). You may or may not find this clearer: (..|select(. == "null")) |= empty (demo) deletes those same keys.


If your values are true nulls, rather than the string "null", you can use the nulls builtin function in place of the whole select: del(..|nulls).

If your values are true nulls and you're using jq 1.5 (the current version in many distributions), you'll need to use recurse(.[]?; true) in place of ... This is because null values are specifically excluded from .. (because it's defined as recurse, which is defined as recurse(.[]?), which is defined as recurse(.[]?; . != null)). This behaviour changed to the (more useful) one above in 1.6, though the documentation hasn't changed, which appears to be a documentation bug.

Antwort2

Assuming that you know you want to test the values at the 2nd level:

jq 'del(.[][] | select(. == "null"))' file

This deletes every key-value pair on the 2nd level from the top, where the value is the literal string null.

Given the document in the question, this produces the expected output.

Using a version of jq newer than 1.5, you could also just update the data to the bits that don't have the null value:

jq '.[][] |= select(. != "null")' file

verwandte Informationen