【问题标题】:Why is adding parentheses to a filter in 'jq' producing valid JSON and without parentheses, multiple outputs of objects?为什么将括号添加到“jq”中的过滤器会产生有效的 JSON 而没有括号,会产生多个对象输出?
【发布时间】:2021-06-17 17:07:25
【问题描述】:

对于jq,我想在 JSON 数据中设置一个属性,让jq 输出原始 JSON 和更新后的值。我或多或少地通过反复试验找到了一个解决方案,并且想了解它为什么以及如何工作。

我有以下 JSON 数据:

{
    "notifications": [
    {
      "source": "observer01",
      "channel": "error",
      "time": "2021-01-01 01:01:01"
    },
    {
      "source": "observer01",
      "channel": "info",
      "time": "2021-02-02 02:02:02"
    }
  ]
}

我的目标是使用特定的 sourcechannel 更新对象的 time 属性(原始 JSON 更长,notifications 数组中有许多相同格式的对象)。

(在下面的例子中,我想用通道info更新observer01time属性,所以上面示例数据中的第二个对象。)

我的第一次尝试是以下jq 命令,但没有产生所需的输出:

jq '.notifications[] | select(.source == "observer01" and .channel == "info").time = "NEWTIME"' data.json

产生以下输出:

{
  "source": "observer01",
  "channel": "error",
  "time": "2021-01-01 01:01:01"
},
{
  "source": "observer01",
  "channel": "info",
  "time": "NEWTIME"
}

这只是notifications 数组中的 JSON 对象列表。我知道这很有用,例如将对象通过管道传输到其他命令行工具。

现在让我们试试下面的jq 命令,和上面的一样加上一对括号:

jq '(.notifications[] | select(.source == "observer01" and .channel == "info").time) = "NEWTIME"' data.json

这会产生所需的输出,即带有更新的 time 属性的原始有效 JSON:

{
    "notifications": [
    {
      "source": "observer01",
      "channel": "error",
      "time": "2021-01-01 01:01:01"
    },
    {
      "source": "observer01",
      "channel": "info",
      "time": "NEWTIME"
    }
  ]
}

为什么在上述情况下将括号添加到jq 过滤器会产生不同的输出?

【问题讨论】:

    标签: json command-line jq


    【解决方案1】:

    括号只是改变优先级。它记录在man jq:

    括号用作分组运算符,就像在任何典型的编程语言中一样。

          jq ´(. + 2) * 5´
             1
          => 15
    

    让我们举一个更简单的例子:

    echo '[{"a":1}, {"a":2}]' | jq '.[] | .a |= .+1'
    

    输出

    {
      "a": 2
    }
    {
      "a": 3
    }
    

    因为它被解释为

                                          ↓         ↓
    echo '[{"a":1}, {"a":2}]' | jq '.[] | (.a |= .+1)'
    

    第一个过滤器.[] 将元素输出为分离的对象,然后由第二个过滤器对其进行修改。

    将括号放在前两个元素之后会改变优先级:

                                    ↓        ↓
    echo '[{"a":1}, {"a":2}]' | jq '(.[] | .a) |= .+1'
    

    并产生不同的输出:

    [
      {
        "a": 2
      },
      {
        "a": 3
      }
    ]
    

    顺便说一句,这与 from 的输出相同

    echo '[{"a":1}, {"a":2}]' | jq '.[].a |= .+1'
    

    它会更改数组中与"a" 键关联的值。

    【讨论】:

    • +1 非常感谢@choroba 的解释,您的示例帮助我理解了这些括号有什么不同。
    【解决方案2】:

    让我们比较一下两者。

     .notifications[] | select(...).time = "NEWTIME"
    
    (.notifications[] | select(...).time) = "NEWTIME"
    

    在第一个中,顶级过滤器由| 定义。输入是一个对象,输出是将select(...).time = "NEWTIME" 应用于.notifications[] 生成的每个值的结果。本质上,原始对象是“丢失”的。

    在第二个中,顶级过滤器由= 定义。 x = y 将其输入作为输出返回,但会产生副作用

    1. 确定路径表达式 x 在输入中所指的内容,
    2. 在输入上评估 过滤器 y,(即使像 "NEWTIME" 这样的表达式也只是一个过滤器:忽略其输入并返回字符串 "NEWTIME"
    3. y 的结果分配给x 寻址的事物。

    【讨论】:

    • +1 谢谢@chepner!虽然@choroba 也发表了一篇出色的文章,但我选择你的作为接受的答案,因为你给出了一些非常有用的深入解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多