81 lines
3.2 KiB
Org Mode
81 lines
3.2 KiB
Org Mode
* [[https://newbedev.com/how-to-hook-emacs-up-to-a-json-service][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:
|
|
|
|
#+begin_example
|
|
(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
|
|
))))
|
|
#+end_example
|
|
|
|
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:
|
|
|
|
#+begin_example
|
|
(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))))
|
|
#+end_example
|
|
|
|
Example usage:
|
|
|
|
#+begin_example
|
|
(my-currency-exchange-rate "USD" "SEK")
|
|
#+end_example
|