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]. 

外部連結

編輯