jq.el.
Decription
Emacs Lisp bindings for jq.
Usage
(require 'jq)
(let ((input "
[
{
\"name\": \"Ness\",
\"age\": 12,
\"origin\": { \"country\": \"Eagleland\", \"town\": \"Onett\" }
},
{
\"name\": \"Paula\",
\"age\": 11,
\"origin\": { \"country\": \"Eagleland\", \"town\": \"Twoson\" }
}
]
"))
(cl-loop for x iter-by (jq input ".[] | .origin.town") collect x))
;;=> ("Onett" "Twoson")
Motivation
Recently I wanted an API client for Emacs which communicate with a RESTful server. I read documents and code of the RESTful server and designed the API client naively by calling the server with curl and jq.
Then, when I would implement the designed client on Emacs Lisp, I remembered that it is a bother to parse JSON strings on Emacs Lisp and I would have to call the server multiple times to implement the client.
Emacs Lisp includes json.el and can parse JSON strings but the results are association lists, so if the fields I am interested in are placed deeply inner the code to retrieve the fields would be very complicated.
Below is a sample where I would want origin.town
of each element from a JSON string input
.
(let* ((input "
[
{
\"name\": \"Ness\",
\"age\": 12,
\"origin\": { \"country\": \"Eagleland\", \"town\": \"Onett\" }
},
{
\"name\": \"Paula\",
\"age\": 11,
\"origin\": { \"country\": \"Eagleland\", \"town\": \"Twoson\" }
}
]
")
(parsed (json-read-from-string input)))
(cl-loop for entry across parsed
collect (let* ((origin (cdr (assoc 'origin entry)))
(town (cdr (assoc 'town origin))))
town)))
;;=> ("Onett" "Twoson")
If my interested fields are placed more deeply inner, the program would be more complicated.
Why I cannot retrieve interested fields of JSON string as simple as the design phase where I could use curl and jq?
This is the motivation that drives me to implement jq.el. With jq.el, we can rewrite the above program as below.
(let ((input "
[
{
\"name\": \"Ness\",
\"age\": 12,
\"origin\": { \"country\": \"Eagleland\", \"town\": \"Onett\" }
},
{
\"name\": \"Paula\",
\"age\": 11,
\"origin\": { \"country\": \"Eagleland\", \"town\": \"Twoson\" }
}
]
"))
(cl-loop for x iter-by (jq input ".[] | .origin.town") collect x))
;;=> ("Onett" "Twoson")
Getting Started
Prerequisites
- cmake
- >= 3.10.0
- other build dependencies
- oniguruma headers, gcc, gcc-c++, libtool, autoconf, yacc & lex and git
To install all build dependencies on Ubuntu, use:
apt install cmake build-essential autoconf libtool bison flex
On Fedora, use:
dnf install cmake make gcc gcc-c++ autoconf libtool bison flex
Building
mkdir build
cd build
cmake ..
cmake --build .
Installing
Doom Emacs
OnAdd the following code to ~/.config/doom/packages.el
.
(package! jq
:recipe (:local-repo "/path/to/jq.el"
:files (
"*.el"
"build/jq-impl.so")))
Spacemacs
OnAdd the following code to dotspacemacs-additional-packages
of ~/.spacemacs
.
dotspacemacs-additional-packages
'(
;; ...
(jq.el :location "/path/to/jq.el/jq.el")
(jq-impl.el :location "/path/to/jq.el/build/jq-impl.el")
)
Emacs Lisp Packages
WithAdd the following code to ~/.init.el
.
(require 'package)
(package-install-file "/path/to/jq.el/jq.el")
(package-install-file "/path/to/jq.el/build/jq-impl.el")
API
(input program)
jq Return results of executing jq by passing input
and program
as arguments.
Development
Running tests
emacs -Q --batch -L build -f batch-byte-compile jq.el \ && emacs -Q --batch -L build -L . -l test/jq-test.el -f ert-run-tests-batch-and-exit
Acknowledgments
- I’m very thankful for comment of @xuchunyang about lack of my understanding.
License
This project is licensed under the MIT License - see the LICENSE.md file for details.