3.2 KiB
How to hook emacs up to a json service? | Newbedev website
[2021-12-18 Sat 14:37]
Article
How to hook emacs up to a json service?
Emacs has pretty good built-in support for this kind of thing, at least at the
basic level of making requests and parsing responses. Use url-retrieve
or
url-retrieve-synchronously
to fetch data from a remote service. As the names
suggest, one is an asynchronous call which takes a callback, the other a
blocking call which returns a buffer containing the response. Load them by
including (require 'url)
in your Elisp file.
The url-
functions are documented in a separate Info manual from the rest of
Elisp, named "URL", but some useful features go unmentioned there. For GET
requests where arguments are passed in the URL, the url-build-query-string
function is useful for constructing a string of query parameters from an a-list
of keys and values. For POST, PUT, DELETE and other requests, you may need to
let-bind the variables url-request-data
, url-request-method
and
url-request-extra-headers
. They have informative docstrings.
A final confusing thing about using these calls for HTTP URLs is that they
leave the HTTP response headers in the same buffer as the response body, which
may not be what you expect. A simple way to deal with this is to use the
(undocumented) url-http-end-of-headers
variable to skip over the headers before
processing the body, though I suspect there might be better ways.
Use the json-read
function to parse JSON responses, and bind the variables
json-array-type
, json-object-type
, and json-key-type
to control how JSON types
are converted to Lisp types. This function is obtained by including (require
'json)
. XML responses can be parsed using xml-parse-region
or
libxml-xml-parse-region
. The latter requires Emacs to be compiled with libxml
support, the former is implemented in Elisp.
Putting this together, the skeleton of a request to a JSON service looks something like this:
(url-retrieve "http://example.com/api/some/request" (lambda (events) (goto-char url-http-end-of-headers) (let ((json-object-type 'plist) (json-key-type 'symbol) (json-array-type 'vector)) (let ((result (json-read))) ;; Do something with RESULT here ))))
In the callback, you can work with result
like any other Lisp value. The
plist-get
, pcase
and cl-destructuring-bind
functions are often useful for
extracting pieces of the result. Use (require 'pcase)
to get the pcase
macro,
(require 'cl-lib)
to get cl-destructuring-bind
.
\\
This is what I use to lookup currency exchange rates from a json webservice at rate-exchange.appspot.com:
(defun my-json-get (url) (interactive) (progn (require 'json) (with-current-buffer (url-retrieve-synchronously url) (goto-char (point-min)) (re-search-forward "^$") (json-read)))) (defun my-currency-exchange-rate (from to) (let ((xurl (format "http://rate-exchange.appspot.com/currency?from=%s&to=%s" from to))) (assoc-default 'rate (my-json-get xurl))))
Example usage:
(my-currency-exchange-rate "USD" "SEK")