toJSON(pi, digits = 4)
[3.1416]
R has 3 packages for working with JSON data
"RJSONIO"
by Duncan Temple Lang
"rjson"
by Alex Couture-Beil
"jsonlite"
by Jeroen Ooms, Duncan Temple Lang, Jonathan Wallace
All packages provide 2 main functions, toJSON()
and fromJSON()
, that allow conversion to and from data in JSON format, respectively. We’ll focus on the functions from "jsonlite"
.
For illustration purposes, let us consider the package "jsonlite"
.
There are 2 primary functions in "jsonlite"
:
toJSON()
converts an R object to a string in JSON
fromJSON()
converts JSON content to R objects
toJSON()
The function jsonlite::toJSON()
converts an R object to a string in JSON.
Let’s begin with a super simple example by passing a single data value to the function toJSON()
:
toJSON(pi, digits = 4)
[3.1416]
Consider the following vectors
<- c(1, 2, 3, 4, 5)
num <- c('a', 'b', 'c', 'd', 'e') lts
Applying toJSON()
to the vectors num
and lts
produces JSON arrays:
toJSON(num)
[1,2,3,4,5]
toJSON(lts)
["a","b","c","d","e"]
The argument pretty = TRUE
allows you to obtain a JSON string with added indentation whitespace:
toJSON(num, pretty = TRUE)
[1, 2, 3, 4, 5]
toJSON(lts, pretty = TRUE)
["a", "b", "c", "d", "e"]
What about an R vector with named elements? For example, here’s a vector vec
<- num
vec names(vec) <- lts
vec
a b c d e
1 2 3 4 5
Converting vec
to JSON, we get:
toJSON(vec)
[1,2,3,4,5]
As you can tell, the names of the elements in vec
are lost in translation.
Here’s another example from an matrix to a JSON array:
<- matrix(9:1, nrow = 3, ncol = 3)
mat mat
[,1] [,2] [,3]
[1,] 9 6 3
[2,] 8 5 2
[3,] 7 4 1
toJSON()
converts an R matrix into a JSON-array
toJSON(mat)
[[9,6,3],[8,5,2],[7,4,1]]
Notice that the returned output arranges the values of the matrix row-by-row, also referred to as row-major. This means that when the input is an R matrix, toJSON()
uses its argument matrix = "rowmajor"
.
You can change the arrangement to column-major by specifying the argument matrix = "columnmajor"
:
toJSON(mat, matrix = "columnmajor")
[[9,8,7],[6,5,4],[3,2,1]]
We can also use toJSON()
on data frames. Here’s an example of an assembled data frame swdf
which will be converted to a JSON-object:
# toy data
<- rbind(
sw_data c("Anakin", "male", "Tatooine", "41.9BBY", "yes"),
c("Amidala", "female", "Naboo", "46BBY", "no"),
c("Luke", "male", "Tatooine", "19BBY", "yes"),
c("Leia", "female", "Alderaan", "19BBY", "no")
)
# convert to data.frame and add column names
<- data.frame(sw_data, stringsAsFactors = FALSE)
swdf names(swdf) <- c("Name", "Gender", "Homeworld", "Born", "Jedi")
swdf
Name Gender Homeworld Born Jedi
1 Anakin male Tatooine 41.9BBY yes
2 Amidala female Naboo 46BBY no
3 Luke male Tatooine 19BBY yes
4 Leia female Alderaan 19BBY no
The default output when you pass a data frame to jsonlite::toJSON()
is
# convert R data.frame to JSON
= toJSON(swdf)
sw_json sw_json
[{"Name":"Anakin","Gender":"male","Homeworld":"Tatooine","Born":"41.9BBY","Jedi":"yes"},{"Name":"Amidala","Gender":"female","Homeworld":"Naboo","Born":"46BBY","Jedi":"no"},{"Name":"Luke","Gender":"male","Homeworld":"Tatooine","Born":"19BBY","Jedi":"yes"},{"Name":"Leia","Gender":"female","Homeworld":"Alderaan","Born":"19BBY","Jedi":"no"}]
The argument dataframe
gives you more control on the output. This argument has three options:
"rows"
: each row is converted to a JSON-object with key-value pairs formed by "column_name": "row_value"
;toJSON(swdf, dataframe = "rows")
[{"Name":"Anakin","Gender":"male","Homeworld":"Tatooine","Born":"41.9BBY","Jedi":"yes"},{"Name":"Amidala","Gender":"female","Homeworld":"Naboo","Born":"46BBY","Jedi":"no"},{"Name":"Luke","Gender":"male","Homeworld":"Tatooine","Born":"19BBY","Jedi":"yes"},{"Name":"Leia","Gender":"female","Homeworld":"Alderaan","Born":"19BBY","Jedi":"no"}]
"columns"
: each column is converted into a JSON-object with a single key for each column, and values stored as arrays;toJSON(swdf, dataframe = "columns")
{"Name":["Anakin","Amidala","Luke","Leia"],"Gender":["male","female","male","female"],"Homeworld":["Tatooine","Naboo","Tatooine","Alderaan"],"Born":["41.9BBY","46BBY","19BBY","19BBY"],"Jedi":["yes","no","yes","no"]}
"values"
: the values in each column are converted to a JSON-array, and the names of the columns are lost.toJSON(swdf, dataframe = "values")
[["Anakin","male","Tatooine","41.9BBY","yes"],["Amidala","female","Naboo","46BBY","no"],["Luke","male","Tatooine","19BBY","yes"],["Leia","female","Alderaan","19BBY","no"]]
fromJSON()
In practice, instead of converting R objects to JSON objects, it is more common to have data in JSON format which needs to be converted into an R object.
The function jsonlite::fromJSON()
converts a JSON-object to an R object.
<- '["computing", "with", "data"]'
json_array
fromJSON(json_array)
[1] "computing" "with" "data"
Consider a simple JSON-object, and its conversion to R with jsonlite::fromJSON()
<- '{"name": "Jessica"}'
json_obj1
fromJSON(json_obj1)
$name
[1] "Jessica"
Notice that the obtained object is an R list in which the key becomes the name of the list, and the value becomes the content of the list’s element.
Consider a less simple JSON-object:
<- '{"name1": "Nicole", "name2": "Pleuni", "name3": "Rori"}'
json_obj2
fromJSON(json_obj2)
$name1
[1] "Nicole"
$name2
[1] "Pleuni"
$name3
[1] "Rori"
Another example:
fromJSON('{"name": ["X", "Y"], "grams": [30, 20], "qty": [4, null],
"new": [true, false]}')
$name
[1] "X" "Y"
$grams
[1] 30 20
$qty
[1] 4 NA
$new
[1] TRUE FALSE
Suppose you have a JSON object with the following data:
{
"Name": ["Anakin","Amidala","Luke","Leia"],
"Gender": ["male","female","male","female"],
"Homeworld": ["Tatooine","Naboo","Tatooine","Alderaan"],
"Born": ["41.9BBY","46BBY","19BBY","19BBY"],
"Jedi": ["yes","no","yes","no"]
}
and assume that the above data is stored as a single (continuous) string in an R character vector json_sw
; applying fromJSON()
to this string gives you the following list:
fromJSON(json_sw)
$Name
[1] "Anakin" "Amidala" "Luke" "Leia"
$Gender
[1] "male" "female" "male" "female"
$Homeworld
[1] "Tatooine" "Naboo" "Tatooine" "Alderaan"
$Born
[1] "41.9BBY" "46BBY" "19BBY" "19BBY"
$Jedi
[1] "yes" "no" "yes" "no"
Can this be transformed into a data frame? Yes, by passing the obtained list to the function data.frame()
:
data.frame(fromJSON(json_sw))
Name Gender Homeworld Born Jedi
1 Anakin male Tatooine 41.9BBY yes
2 Amidala female Naboo 46BBY no
3 Luke male Tatooine 19BBY yes
4 Leia female Alderaan 19BBY no
Now that we have discussed the basics of JSON, and the common ways to convert fromJSON()
and toJSON()
, let’s see how to read JSON data from the Web.
One of the typical ways to import JSON data from the Web to R is by passing the url directly to fromJSON()
. Another way is by passing the name of the file with the JSON content as a single string to the function fromJSON()
.
Here’s an example reading a JSON string from the website Advice Slip. The url https://api.adviceslip.com/advice gives you a random advice (see figure below):
As you can tell, the content is a simple JSON string
<- "https://api.adviceslip.com/advice"
advice_url
fromJSON(advice_url)
$slip
$slip$id
[1] 9
$slip$advice
[1] "True happiness always resides in the quest!"
The following data comes from one of Dave Eddy’s github repositories:
https://raw.githubusercontent.com/bahamas10/css-color-names/master/css-color-names.json
This is a JSON-object in which the keys are color-names, and the values are the hexadecimal digits of the corresponding color:
{
"aliceblue": "#f0f8ff",
"antiquewhite": "#faebd7",
"aqua": "#00ffff",
"aquamarine": "#7fffd4",
"azure": "#f0ffff",
...
"wheat": "#f5deb3",
"white": "#ffffff",
"whitesmoke": "#f5f5f5",
"yellow": "#ffff00",
"yellowgreen": "#9acd32"
}
We pass the url to jsonlite::fromJSON()
<- "https://raw.githubusercontent.com/bahamas10/css-color-names/master/css-color-names.json"
colors_json
<- fromJSON(colors_json) hex_colors
The output in hex_colors
is a list with 148 elements; the first five elements are displayed below:
1:5] hex_colors[
$aliceblue
[1] "#f0f8ff"
$antiquewhite
[1] "#faebd7"
$aqua
[1] "#00ffff"
$aquamarine
[1] "#7fffd4"
$azure
[1] "#f0ffff"