Converting long DOIs to short DOIs
The shortDOI Service
When writing a paper recently, I came across the shortDOI service that creates short aliases for long DOIs. For example, instead of a long DOI like 10.1016/j.virusres.2019.197847
you can now have 10/g8r7pm
It will either return an existing alias or create a new one if required.
(And it helped me reduce the paper length by half a page 💪)
Best part is they have a simple public API to create aliases. So, for the above long DOI, we simply append the DOI, and the desired format (either xml or json) to the URL as follows:
https://shortdoi.org/10.1016/j.virusres.2019.197847?format=json
And you get a response like below with the short DOI:
{
"DOI": "10.1016/j.virusres.2019.197847",
"ShortDOI": "10/g8r7pm",
"IsNew": false
}
Automating with Go
I wanted to automate this for all my bib files. So I ended up writing a simple Go program to do this for me. The code is on GitHub.
This also gave me an opportunity to learn several core Go packages as part of my Go learning journey. The first lesson is to use the http
package to call the API.
// Return the short doi received from shortdoi.org for long `doi`.
func GetShortDOI(doi string) string {
= strings.ReplaceAll(doi, `\`, "")
doi , err := http.Get(URL + doi + "?format=json")
respif err != nil {
panic(err)
}
defer resp.Body.Close()
var result response
, err := io.ReadAll(resp.Body)
bodyif err := json.Unmarshal(body, &result); err != nil {
.Println(doi)
fmtpanic(err)
}
return result.ShortDOI
}
and then use goroutines
and WaitGroups
to asynchronously call the API for all long DOIs in a file.
// Get short DOIs for each DOI found in the file `f`.
// Returns a map of LongDOI -> ShortDOI.
func getShortDOIs(f *os.File) map[string]string {
:= regexp.MustCompile(`10\.\d{4,9}/[-.\\_;()/:A-Za-z0-9]+`)
r
var wg sync.WaitGroup
:= sync.RWMutex{}
lock := make(map[string]string)
doiMap
:= bufio.NewScanner(f)
s for s.Scan() {
:= s.Text()
line := r.FindString(line)
m if len(m) > 0 {
.Add(1)
wggo func(doi string) {
defer wg.Done()
:= GetShortDOI(doi)
shortDoi
.Lock()
lockdefer lock.Unlock()
[doi] = shortDoi
doiMap}(m)
}
}
.Wait()
wg
return doiMap
}
Now if I run the executable as:
./short-doi -i ref.bib -o short-doi-ref.bib
all the long DOIs are replaced with their aliases.
Before | After |
note = {doi: {10.1016/j.cirpj.2020.02.002}}, note = {doi: {10.1016/j.cropro.2019.05.015}}, note = {doi: 10.1016/j.tree.2023.04.010}, note = {doi: {10.1007/978-1-4020-8992-3\_3}}, note = {doi: {10.3897/rio.10.e125167}}, note = {doi: {10.5751/ES-03400-150213}}, note = {doi: {10.1016/j.envsoft.2017.01.014}}, note = {doi: {10.1007/s13593-015-0327-9}}, |
note = {doi: {10/ghg846}}, note = {doi: {10/grxgxg}}, note = {doi: 10/nqnv}, note = {doi: {10/dvstgm}}, note = {doi: {10/g8r7pn}}, note = {doi: {10/ggr75q}}, note = {doi: {10/gfvzwx}}, note = {doi: {10/f7x4xt}}, |