emacs.d/emacs_json.org

82 lines
3.2 KiB
Org Mode
Raw Normal View History

2021-12-20 09:51:16 +01:00
* [[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