Handling nulls in nested object maps with JQ

Viewed 78

I'm just learning jq, and a bit stumped here. Suppose I've got a file entitled testcite.json like this:

[
  {
    "id": "JoeSchmoe2020",
    "abstract": "Here's some junk",
    "author": [
      {
        "family": "Scmoe",
        "given": "Joe"
      },
      {
        "family": "Smith",
        "given": "Sally"
      }
    ],
    "title": "I wrote words!"
  },
  {
    "id": "EdithJones2020",
    "abstract": "It's an abstract",
    "author": [
      {
        "family": "Jones",
        "given": "Edith"
      },
      {
        "family": "Wilson",
        "given": "Eric"
      }
    ],
    "title": "These are more words!"
  }
]

and suppose what I want to convert it to is something like this:

[
  {
    "author": [
      "Scmoe",
      "Smith"
    ],
    "title": "I wrote words!"
  },
  {
    "author": [
      "Jones",
      "Wilson"
    ],
    "title": "These are more words!"
  }
]

The following command works to get me what I want... so long as there aren't any nulls.

cat testcite.json | jq '[.[] | {author: .author | map(.family), title}]'

But the moment there's a null, this blows up. Suppose I change my JSON:

[
  {
    "id": "JoeSchmoe2020",
    "abstract": "Here's some junk",
    "author": [
      {
        "family": "Scmoe",
        "given": "Joe"
      },
      {
        "family": "Smith",
        "given": "Sally"
      }
    ],
    "title": "I wrote words!"
  },
  {
    "id": "EdithJones2020",
    "abstract": "It's an abstract",
    "author": null,
    "title": "These are more words!"
  }
]

then when I run the same command, what I would expect to get would be something like

[
  {
    "author": [
      "Scmoe",
      "Smith"
    ],
    "title": "I wrote words!"
  },
  {
    "author": [],
    "title": "These are more words!"
  }
]

(I'd also be happy with a null in place of the empty list).

Unfortunately, instead, I get the error:

jq: error (at <stdin>:23): Cannot iterate over null (null)

I've tried scattering in question marks:

cat testcite.json | jq '[.[] | {author: .author | map(.family?), title}]' just produces the same error.

cat testcite.json | jq '[.[] | {author: .author | map(.family)?, title}]' produces a syntax error:

jq: error: syntax error, unexpected '?', expecting '}' (Unix shell quoting issues?) at <top-level>, line 1:
[.[] | {author: .author | map(.family)?, title}]
jq: 1 compile error

Is there any way I can get the value extraction to just handle nulls sensibly?

1 Answers

A straightforward solution would be to test for null, e.g.

[.[]
 | {author, title}
 | .author |= if . then map(.family) else [] end
]

or similarly:

map({author: (.author 
              | if type == "array" 
                then map(.family) 
                else [] end),
     title} )
Related