我有一个具有独立JSON对象(即不是数组)的输入文件,我想从每个对象中过滤几个字段,并使用结果元素创建一个数组。它基本上是JSON格式的日志语句列表。
为此,我正在使用jq
,它工作得很好,除了不能将所有结果对象聚合到单个数组中。
输入内容如下:
{"name":"myname", "environment":"staging", "email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}
{"name":"myname", "environment":"staging", "email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}
...
{"name":"myname", "environment":"staging", "email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}
{"name":"myothername", "environment":"staging", "time":"2017-10-02T05:00:00.046Z"}
(请注意,最后一项没有email
字段,因此如果未过滤,它将返回null
值)
从该对象列表中,我只想获取字段email
和time
,而忽略其余字段,因此我使用以下jq
查询:
jq '{email: (.email | values), time: (.time | values)}' input.json
请注意,由于日志消息是混合的,因此我使用values
过滤器,因此并非所有json对象都具有email
字段,因此我将其忽略。
我的问题是,即使我得到了期望的结果,我仍然得到一个列表,并且我想要一个数组。
即我得到类似
{"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}
{"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}
...
{"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}
我想要它:
[
{"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"},
{"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"},
...,
{"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}
]
我尝试了几种不同的方法,但是通常会遇到错误Cannot index array with string "email"
,该错误告诉我数组操作出错了。
我尝试将查询包装在map()
中,即map({.userEmail, .time})
,尝试使用-s
对数据进行筛选,并尝试使用|+
和|=
运算符。
我也尝试过将查询包装在[{email: (.email|values), time:.time }]
之类的数组括号内,但是得到的对象却相同,只是每个对象本身都包装在数组中,即
[{"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}]
[{"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}]
...
[{"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}]
这似乎很容易做,或者至少是一个普通的操作,但是我找不到正确的查询。
当输入不是数组时,将查询结果聚合到数组中的正确方法是什么?
答案 0 :(得分:1)
更好...
根据示例数据,您的基本过滤器可以简化为{email, time}
通常,最好避免“模糊处理”输入(例如,以节省内存)。在您的情况下,可以通过将inputs
与-n命令行选项一起使用来实现。
将它们放在一起:
jq -n '[inputs | {email, time }]' input.json
如果要过滤掉某些输入,则可以使用select
,例如
jq -n '[inputs | select(.email) | {email, time } ]' input.json
答案 1 :(得分:0)
阅读更多内容后,我发现了想要的结果,它是slurp运算符和map的组合。
我意识到查询
jq -s 'map({email: (.email|values), time:.time })' input.json
将所有输入项读取为数组,然后读取为per the definition of map():
对于任何过滤器x,map(x)将对输入数组的每个元素运行该过滤器,并将输出返回到新数组中
所以两者结合起来就给了我所需的结果。
答案 2 :(得分:0)
对于那些喜欢替代(即非jq)解决方案的人,以下是一个基于JSON的步行路径unix工具的解决方案: jtc
:
inputs.json
: bash $ jtc -w'[email]<e>v[-1][time]' -T'{"email":{{e}},"time":{{}}}' -J inputs.json
[
{
"email": "email1@example.com",
"time": "2017-04-02T05:00:00.046Z"
},
{
"email": "email2@example.com",
"time": "2017-02-02T05:00:00.046Z"
},
{
"email": "email3@example.com",
"time": "2017-10-02T05:00:00.046Z"
}
]
bash $
这里的步行路径(-w
)很简单:首先用email
标记地址,然后将值存储到命名空间e
中,然后在JSON中上一级,并用{寻址{1}}条记录。
然后让输出经过模板迭代(time
),对存储的值-T
和最后一个遍历的{{e}}
进行插值,该时间恰好是时间戳。
{{}}
将所有遍历收集到JSON数组中
PS>披露:我是-J
工具的创建者