Converting a doi to a bibtex entry

| categories: bibtex | tags:

Many citation management packages allow you to download a bibliography entry from a doi. I want to be able to do that in emacs. I found this page that shows it is possible to get metadata about a doi with an http request, and from that data, we can create a bibtex entry. So, here is the basic code for getting metadata about a doi. We specify that we want json code, and then use json.el to view the results.

We temporarily set a few url-* variables with affect the url-retrieve results. And we rely on url-http-end-of-headers which tells us the end of the headers that get returned, so we can use the remaining text as the data.

(require 'json)

(let ((url-request-method "GET")
      (url-mime-accept-string "application/citeproc+json")
      (json-object-type 'plist)
      (results))
  (setq results
	(with-current-buffer (url-retrieve-synchronously "https://doi.org/10.1016/S0022-0248(97)00279-0")
	  (json-read-from-string (buffer-substring url-http-end-of-headers (point-max))))))

(:volume 181 :indexed (:timestamp 1389218884442 :date-parts 2014 1 8) :publisher Elsevier BV :source CrossRef :URL https://doi.org/10.1016/S0022-0248(97) 00279-0 :ISSN [0022-0248] :DOI 10.1016/s0022-0248(97)00279-0 :type journal-article :title Effect of growth conditions on formation of TiO2-II thin films in atomic layer deposition process :issue 3 :deposited (:timestamp 1386028800000 :date-parts 2013 12 3) :page 259-264 :reference-count nil :container-title Journal of Crystal Growth :author [(:given Jaan :family Aarik) (:given Aleks :family Aidla) (:given Väino :family Sammelselg) (:given Teet :family Uustare)] :prefix http://id.crossref.org/prefix/10.1016 :score 1.0 :issued (:date-parts 1997 11) :subject [Condensed Matter Physics Inorganic Chemistry Materials Chemistry] :subtitle [])

That data is now sufficient for us to consider constructing a bibtex entry. For an article, a prototypical entry looks like:

@Article{,
  author = 	 {},
  title = 	 {},
  journal = 	 {},
  year = 	 {},
  OPTkey = 	 {},
  OPTvolume = 	 {},
  OPTnumber = 	 {},
  OPTpages = 	 {},
  OPTmonth = 	 {},
  OPTnote = 	 {},
  OPTannote = 	 {}
}

Let us create a function that takes a doi and constructs a bibtex entry. I do not use all the metadata, so I just store the json data in the annote field. Maybe I should use another field for that, but annote seems ok since I do not use if for anything. I am going to use a template expansion function I developed earlier to make the bibtex entry template easier to write and read. Here is the code.

(require 'json)

(defun expand-template (s)
  "expand a template containing %{} with the eval of its contents"
  (replace-regexp-in-string "%{\\([^}]+\\)}"
                            (lambda (arg)
                              (let ((sexp (substring arg 2 -1)))
                                (format "%s" (eval (read sexp))))) s))

(defun doi-to-bibtex-article (doi)
 "insert a bibtex entry for doi at point"
 (interactive "sDOI: ")
 (let ((url-request-method "GET")
       (url-mime-accept-string "application/citeproc+json")
       (json-object-type 'plist)
       type
       results
       author
       title
       journal
       year
       volume
       number
       pages
       month
       url json-data)

   (setq results
	 (with-current-buffer
	     (url-retrieve-synchronously
	      (concat "https://doi.org/" doi))
	 (json-read-from-string (buffer-substring url-http-end-of-headers (point-max))))
         type (plist-get results :type)
	 author (mapconcat (lambda (x) (concat (plist-get x :given) " " (plist-get x :family)))
		     (plist-get results :author) " and ")
	 title (plist-get results :title)
	 journal (plist-get results :container-title)
	 volume (plist-get results :volume)
	 issue (plist-get results :issue)
	 year (elt (elt (plist-get (plist-get results :issued) :date-parts) 0) 0)
	 month (elt (elt (plist-get (plist-get results :issued) :date-parts) 0) 1)
	 pages (plist-get results :page)
	 doi (plist-get results :DOI)
	 url (plist-get results :URL)
	 json-data (format "%s" results))

   (when (string= type "journal-article")

     (expand-template "@article{,
  author = 	 {%{author}},
  title = 	 {%{title}},
  journal = 	 {%{journal}},
  year = 	 {%{year}},
  volume = 	 {%{volume}},
  number = 	 {%{issue}},
  pages = 	 {%{pages}},
  doi =          {%{doi}},
  url =          {%{url}},
  month = 	 {%{month}},
  json = 	 {%{json-data}}
}"))))

(doi-to-bibtex-article "10.1016/s0022-0248(97)00279-0")
@article{,
  author = 	 {Jaan Aarik and Aleks Aidla and Väino Sammelselg and Teet Uustare},
  title = 	 {Effect of growth conditions on formation of TiO2-II thin films in atomic layer deposition process},
  journal = 	 {Journal of Crystal Growth},
  year = 	 {1997},
  volume = 	 {181},
  number = 	 {3},
  pages = 	 {259-264},
  doi =          {10.1016/s0022-0248(97)00279-0},
  url =          {https://doi.org/10.1016/s0022-0248(97)00279-0},
  month = 	 {11},
  json = 	 {(:volume 181 :indexed (:timestamp 1389218884442 :date-parts [[2014 1 8]]) :publisher Elsevier BV :source CrossRef :URL https://doi.org/10.1016/s0022-0248(97)00279-0 :ISSN [0022-0248] :DOI 10.1016/s0022-0248(97)00279-0 :type journal-article :title Effect of growth conditions on formation of TiO2-II thin films in atomic layer deposition process :issue 3 :deposited (:timestamp 1386028800000 :date-parts [[2013 12 3]]) :page 259-264 :reference-count nil :container-title Journal of Crystal Growth :author [(:given Jaan :family Aarik) (:given Aleks :family Aidla) (:given Väino :family Sammelselg) (:given Teet :family Uustare)] :prefix http://id.crossref.org/prefix/10.1016 :score 1.0 :issued (:date-parts [[1997 11]]) :subject [Condensed Matter Physics Inorganic Chemistry Materials Chemistry] :subtitle [])}
}

That looks excellent. Note there are some non-ascii characters in it, which would have to be fixed. Let us try it on an ASAP article.

(doi-to-bibtex-article "10.1021/ie403744u")
@article{,
  author = 	 {José A. Delgado and V. I. Águeda and M. A. Uguina and J. L. Sotelo and P. Brea and Carlos A. Grande},
  title = 	 { Adsorption and Diffusion of H 2 , CO, CH 4 , and CO 2 in BPL Activated Carbon and 13X Zeolite: Evaluation of Performance in Pressure Swing Adsorption Hydrogen Purification by Simulation },
  journal = 	 {Industrial & Engineering Chemistry Research},
  year = 	 {2014},
  volume = 	 {nil},
  number = 	 {nil},
  pages = 	 {140117091024005},
  doi =          {10.1021/ie403744u},
  url =          {https://doi.org/10.1021/ie403744u},
  month = 	 {1},
  json = 	 {(:indexed (:timestamp 1392935578089 :date-parts [[2014 2 20]]) :publisher American Chemical Society (ACS) :source CrossRef :URL https://doi.org/10.1021/ie403744u :ISSN [0888-5885 1520-5045] :DOI 10.1021/ie403744u :type journal-article :title  Adsorption and Diffusion of H 2 , CO, CH 4 , and CO 2 in BPL Activated Carbon and 13X Zeolite: Evaluation of Performance in Pressure Swing Adsorption Hydrogen Purification by Simulation  :deposited (:timestamp 1389916800000 :date-parts [[2014 1 17]]) :page 140117091024005 :reference-count nil :container-title Industrial & Engineering Chemistry Research :author [(:given José A. :family Delgado) (:given V. I. :family Águeda) (:given M. A. :family Uguina) (:given J. L. :family Sotelo) (:given P. :family Brea) (:given Carlos A. :family Grande)] :prefix http://id.crossref.org/prefix/10.1021 :score 1.0 :issued (:date-parts [[2014 1 17]]) :subject [Chemistry(all) Industrial and Manufacturing Engineering Chemical Engineering(all)] :subtitle [])}
}

You see that nil is put in for missing entries. That is probably ok. There is an & in the journal that needs to be cleaned up, but that is easily done with org-ref-clean-bibtex-entry. In summary, this looks like a very convenient way to get bibtex entries inside emacs. I should probably have the function insert that string to a buffer at point, but that is not difficult to do.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.6

Discuss on Twitter