jq领域特定高级词法作用域函数式编程语言,在其中所有JSON值都是常量。jq支持回溯并可管理JSON数据的无限长字串流。jq支持基于名字空间的模块系统,并对闭包有一定支持,尤其是它的函数和泛函表达式可以用作其他函数的参数。

jq
jq官方图标
编程范型纯函数式, 面向JSON处理, 隐式
设计者Stephen Dolan
发行时间2012年8月21日,​12年前​(2012-08-21
当前版本
  • 1.7.1(2023年12月13日;稳定版本)[1]
编辑维基数据链接
实作语言jq:C
gojq:Go
jaq:Rust
jqjq:jq
系统平台跨平台[a]
操作系统跨平台[b]
许可证MIT[c]
网站jqlang.github.io/jq
启发语言
JSON, Unix shell, sed, Icon

jq与IconHaskell编程语言有关。它最初采用Haskell实现[3],随即移植至C语言。

历史

编辑

jq由Stephen Dolan创建并在2012年10月发行[4][5] 。它被设计为“针对JSON数据的sed类似者”[6]。在jq版本1.5中增加支持了正则表达式

针对jq的叫做yq的“包装器”[7],增加支持了YAMLXMLTOML。它首次发行于2017年[8]

Go实现的gojq最初发行于2019年[9],gojq显著的扩展jq包括了支持YAML

Rust实现的jaq,其项目目标是更快速和更准确的jq实现,仍保持与jq在大多数情况下的兼容性。在2024年3月于其目标中排除了jq的特定高级特征,比如模块、SQL风格算子和给非常大JSON文档的串流解析器[10]

用jq实现的jqjq,最初发行于2022年。jqjq显著的可以运行自身,拥有REPL并支持eval

用法

编辑

命令行用法

编辑

jq典型的用于命令行,并可以协作于其他命令行实用工具,比如curl。下面的例子展示如何将curl命令的输出通过管道接转到jq过滤器英语Filter (software),从而确定同这个Wikipedia页面关联的范畴名字:

$ URL='https://zh.wikipedia.org/w/api.php?action=parse&page=Jq%E8%AF%AD%E8%A8%80&format=json'
$ curl -s ${URL} | jq '.parse.categories[]."*"'

这里的流水线产生的输出,由JSON字符串的串流组成,它们是:

"小寫標題"
"CS1英语来源_(en)"
"动态类型编程语言"
"函数式编程语言"
"面向文本编程语言"
"2012年建立的程式語言"
"数据查询语言"
"2012年软件"

上述curl命令对这个页面使用了MediaWiki API来产生JSON响应。管道(pipe)符号|允许curl的输出由jq来访问,它是标准的Unix shell进程间通信机制[11]

这里展示的jq过滤器的方法链是如下流水线(pipeline)的简写:

.["parse"] | .["categories"] | .[] | .["*"]

这对应于curl调用所产生的嵌套JSON结构。jq流水线的构造方式,同Unix风格流水线一样,采用管道符号|

嵌入式用法

编辑

C语言和Go实现二者都提供函数库,使得jq功能可以嵌入到其他应用和编程环境之中。

例如,gojq已经集成于SQLite,故而jq函数可以在其SQL语句中获得到[12]。这些函数被标记为“确定性的”[13],故而可以被用在CREATE INDEX命令中[14]

运算的模态

编辑

jq缺省的充当针对JSON输入的“串流编辑器”,非常像被当作多行文本的“串流编辑器”的sed实用工具。但是jq有一些其他运算模态:

  1. 它可以将来自一个或多个来源的输入当作文本的诸行;
  2. 它可以将来自特定来源的输入的串流收集到一个JSON阵列之中;
  3. 它可以使用所谓的“串流解析器”解析其输入,产生针对所有“叶子”路径的[path, value]阵列的串流。

“串流解析器”(streaming parser),在一个或多个JSON输入太大无法载入内存之时特别有用,因为它需求的内存典型的相当小。例如,对于任意大的JSON对象的阵列,峰值内存需求不比处理最大顶层对象所需要的多出很多。

这些运算模态可以在特定限制下组合起来。

语法和语义

编辑

类型

编辑

所有JSON值自身是jq中的值,它们从而有在下列表格中展示的类型[15]。gojq和jaq实现将区分为整数浮点数。gojq实现支持无界精度整数算术,同于jq采用Haskell的最初实现。

jq支持的类型总结
类型 例子
"number"
  • 3
  • 3.2
  • 1e6
  • nan
  • infinite
"string"
  • "Hello"
  • "😐"
"boolean"
  • true
  • false
"array"
  • [1, "2", {"mixed": "type"}, [3,4]]
"object"
  • {"one": 1, "two": "2", "three": [3]}
"null"
  • null

null是一个值[16],就像任何其他JSON标量一样;它不是指针空指针nan(对应于NaN)和infinite(参见IEEE 754),是仅有的两个不是JSON值的jq标量。

形式

编辑

对于函数创建、条件、串流归约和模块系统,分别有特殊语法形式:defif~then~else~endreduceimport~as

过滤器

编辑

jq是面向JSON的编程语言,使用|符号来连接过滤器形成流水线。例如:

$ echo '[1,2]' | jq 'add'
3
$ jq -n '[1,2] | add'
3

这里jq内的JSON阵列[1,2]是求值为阵列的一个jq过滤器。

尽管类似于Unix流水线,jq流水线允许将到来数据,如同并行的发送到在|右手端的多于一个接收者。例如,程序add/length将计算阵列中数的平均,故而:

$ jq -n '[1,2] | add/length'
1.5
$ jq -nc '[1,2] | [length, add, add/length]'
[2,3,1.5]

点号.可以被用来在右手端定义一个附着点(attachment point),例如:

$ jq -nc '1 | [., .]'
[1,1]
$ jq -n '2 | pow(.; .)'
4

下面例子是隐式编程风格生成斐波那契数列

$ jq -c '[limit(.; [0,1] | recurse([last, add]) | last)]'
0
[]
1
[1]
2
[1,1]
6
[1,1,2,3,5,8]
$ jq -c '[limit(.+1; [0,1] | recurse([last, add]) | first)]'
0
[0]
1
[0,1]
2
[0,1,1]
6
[0,1,1,2,3,5,8]
$ jq 'nth(.; [0,1] | recurse([last, add]) | first)'
0
0
6
8
$ jq -n 'first([0,1] | recurse([last, add]) | first)'
0

下面将其定义为新的命名过滤器:

def fib:
    [limit(.; [0,1] | recurse([last, add]) | last)];

下面的例子展示如何定义命名的参数化的过滤器,它格式化从2到36含二者的任何底数的整数:

def tobase($b):
    def digit: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[.:.+1];
    def mod: . % $b;
    def div: ((. - mod) / $b);
    def place_values: recurse(select(. >= $b) | div) | mod;
    select(2 <= $b and $b <= 36)
    | [place_values | digit] | reverse | add;

将其保存入tobase.jq文件,接着使用这个函数:

$ jq 'include "./tobase"; tobase(16)'
16
"10"

下一个例子求解经典的覆面算数学游戏

 

M是在最高位的进位可知:M1S89O0,代码展示了基于range().[]生成器

def send_more_money:
    def choose(m;n;used): ([range(m;n+1)] - used)[];
    def num(a;b;c;d): 1000*a + 100*b + 10*c + d;
    def num(a;b;c;d;e): 10*num(a;b;c;d) + e;
    1 as $m | 0 as $o
    | choose(8;9;[]) as $s
    | choose(2;9;[$s]) as $e
    | choose(2;9;[$s,$e]) as $n
    | choose(2;9;[$s,$e,$n]) as $d
    | choose(2;9;[$s,$e,$n,$d]) as $r
    | choose(2;9;[$s,$e,$n,$d,$r]) as $y
    | select(num($s;$e;$n;$d) 
      +      num($m;$o;$r;$e)
      ==  num($m;$o;$n;$e;$y))
    | [$s,$e,$n,$d,"+",$m,$o,$r,$e,"=",$m,$o,$n,$e,$y];
send_more_money

将上述代码保存入send_more_money.jq文件,接着使用这个函数得到这个谜题仅有的一个解:

$ jq -nc -f ./send_more_money.jq
[9,5,6,7,"+",1,0,8,5,"=",1,0,6,5,2]

解析表达式文法

编辑

在jq和解析表达式文法(PEG)形式化之间有密切关联[17]。这种关联源于下列表格中展示的PEG七个基本运算与jq构造之间的等价性。

PEG运算与对应的jq等价者
PEG运算名字 PEG表示法 jq运算或def
序列 e1 e2 e1 | e2
有序选择 e1 / e2 e1 // e2
零或多个 e* def star(E): (E | star(E)) // .;
一或多个 e+ def plus(E): E | (plus(E) // .);
可选 e? def optional(E): E // .;
与断言 &e def amp(E): . as $In | E | $In;
非断言 !e def neg(E): select([E] == []);

移植和变体

编辑

gojq是“纯Go”实现。还有Rust实现的jq方言叫做jaq[10],它规定了指称语义[18]

注释

编辑
  1. ^ jq的C或Go实现都没有任何运行时间依赖[2]
  2. ^ 包括WindowsLinuxmacOS。Go实现可以在Go所支持的任何平台上编译[2]
  3. ^ jq的C实现,使用了叫做decNumber的十进制浮点数库,它采用了ICU许可证;和Oniguruma正则表达式库,它采用了BSD许可证[2]

参考书目

编辑

引用

编辑
  1. ^ Release jq 1.7.1. 
  2. ^ 2.0 2.1 2.2 Download jq. jq. [January 6, 2023]. 
  3. ^ Initial · jqlang/Jq@eca89ac. GitHub. 
  4. ^ Janssens 2014.
  5. ^ jq. jq. [January 6, 2023]. 
  6. ^ like sed. (原始内容存档于2013-04-14). 
  7. ^ yq
  8. ^ Release v2.0.0 · kislyuk/yq. GitHub. 
  9. ^ Release v0.0.1 · itchyny/gojq. GitHub. 
  10. ^ 10.0 10.1 01mf02/jaq: A jq clone focussed on correctness, speed, and simplicity. GitHub. [March 6, 2024]. 
  11. ^ Tutorial. jq. [January 6, 2023]. 
  12. ^ sqlite_jq. GitHub. 
  13. ^ "deterministic"
  14. ^ FAQ. GitHub. 
  15. ^ Manual. jq. [January 6, 2023]. 
  16. ^ [https://www.json.org/json-en.html null
  17. ^ PEG. PEG. 
  18. ^ Färber, Michael. Denotational Semantics and a fast interpreter for jq. 2023. arXiv:2302.10576  [cs.LO]. 

外部链接

编辑