GoT Characters

  collapse = TRUE,
  comment = "#>"
got_json <- got_chars_json


First, let's find out what's the type of the JSON


With json_array_length() and json_array_types() we can find out the number of elements in the array and the type of each element:


Each element of the array is a JSON object. To work with the data in the array it is often more convenient to unpack it with json_flatten(). This produces a JSON vector whose elements are the elements of the array:


In non-interactive use it is a good idea to specify the type you expect via the ptype argument. In this case we expect each element to be a JSON object and therefore use ptype = new_json_object().

got_chars <- json_flatten(got_json, ptype = new_json_object())

An alternative would have been to use ptype = json2() when we don't care whether the elements are arrays, objects, or a mix of them.

Extract Data

Let's have a look at the first Game of Thrones character


This minified JSON is not so easy to read so we prettify it with json_prettify()


With the help of json_extract() we can now easily extract values from each character:

json_extract(got_chars, "$.name")

The second argument "$.name" specifies the path of the element we want to extract. The basic syntax is relatively simple:

If you wonder about the dollar sign $: it stands for the current element. Simply always start the path with it and you will be fine.

To extract elements of nested objects you simply combine the path. For example the path to 1 in {"a": {"b": [1, 2]}} is $.a.b[0]. You can find more examples in article JSONpath - XPath for JSON by Stefan Gössner.

We can now construct a tibble with some basic information about the characters

  id = json_extract(got_chars, "$.id"),
  name = json_extract(got_chars, "$.name"),
  alive = json_extract(got_chars, "$.alive")

Oh, we actually also wanted the titles of each character

  id = json_extract(got_chars, "$.id"),
  name = json_extract(got_chars, "$.name"),
  alive = json_extract(got_chars, "$.alive"),
  titles = json_extract(got_chars, "$.titles")

Unfortunately, we get an error message saying that we cannot combine an array with text. Let's try extracting the books as text:

json_extract(got_chars, "$.titles", ptype = character())

We see that the API actually isn't type stable: it returns a simple text value instead of an array if the character only has one title. It is usually not a good idea to mix characters and arrays in an API. But as some APIs in the wild do this you can fix it with the argument wrap_scalars and get only JSON arrays back:

json_extract(got_chars, "$.titles", wrap_scalars = TRUE)


It would be quite tedious to extract every field of a character like that. Instead you can simply use json_unnest_wider() to convert the keys of a JSON object into columns. Like before, we also have to use wrap_scalars = TRUE:

got_chars_df <- tibble::tibble(chars_json = got_chars) %>%
  json_unnest_wider(chars_json, wrap_scalars = TRUE)


We can also unnest arrays with json_unnest_longer(). This is basically a version json_flatten() for data frames

got_chars_df[c("id", "name", "titles")] %>% 

Modify Data

It is also possible to modify the JSON directly. With json_delete() we can delete multiple fields in an object

got_chars_small <- json_delete(got_chars, "$.url", "$.aliases", "$.allegiances")

And with json_mutate() we can easily mutate elements

  .id = 1:5,
  .alive = !json_extract(got_chars_small, "$.alive")

or patch with another JSON using json_merge()

x <- c(
  '{"id": 1, "a": 3}',
  '{"id": 2, "a": 4}',
  '{"id": 3}'

# remove element at "a"
json_merge(x, '{"a": null}')

# it is vectorised
    '{"a": null}',
    '{"a": 5}',
    '{"a": 6}'

Try the jsontools package in your browser

Any scripts or data that you put into this service are public.

jsontools documentation built on March 22, 2021, 5:06 p.m.