HTML & JSON

Introduction

For this project, two files have been created holding the same information on R Programming textbooks. One is a JSON file and the other is an HTML file. Both have the same information and we will be comparing the two to see how each file type handles data.

library(rvest)
library(jsonlite)
library(tidyverse)

Creating the data frames

Using the rvest and jsonlite package we can load in each files as a tibble, and view them separately

html_df <- read_html("books.html") |>
  html_element("table") |>
  html_table() |>
  as_tibble()

html_df
# A tibble: 3 × 5
  title                    authors                          year publisher isbn 
  <chr>                    <chr>                           <int> <chr>     <chr>
1 R for Data Science       Hadley Wickham, Mine Çetinkaya…  2023 O'Reilly… 978-…
2 Advanced R               Hadley Wickham                   2019 CRC Press 978-…
3 The Art of R Programming Norman Matloff                   2011 No Starc… 978-…
str(html_df)
tibble [3 × 5] (S3: tbl_df/tbl/data.frame)
 $ title    : chr [1:3] "R for Data Science" "Advanced R" "The Art of R Programming"
 $ authors  : chr [1:3] "Hadley Wickham, Mine Çetinkaya-Rundel, Garrett Grolemund" "Hadley Wickham" "Norman Matloff"
 $ year     : int [1:3] 2023 2019 2011
 $ publisher: chr [1:3] "O'Reilly Media" "CRC Press" "No Starch Press"
 $ isbn     : chr [1:3] "978-1492097402" "978-0815384571" "978-1593273842"
json_df <- fromJSON("books.json") |>
  as_tibble()

json_df
# A tibble: 3 × 5
  title                    authors    year publisher       isbn          
  <chr>                    <list>    <int> <chr>           <chr>         
1 R for Data Science       <chr [3]>  2023 O'Reilly Media  978-1492097402
2 Advanced R               <chr [1]>  2019 CRC Press       978-0815384571
3 The Art of R Programming <chr [1]>  2011 No Starch Press 978-1593273842
str(json_df)
tibble [3 × 5] (S3: tbl_df/tbl/data.frame)
 $ title    : chr [1:3] "R for Data Science" "Advanced R" "The Art of R Programming"
 $ authors  :List of 3
  ..$ : chr [1:3] "Hadley Wickham" "Mine Çetinkaya-Rundel" "Garrett Grolemund"
  ..$ : chr "Hadley Wickham"
  ..$ : chr "Norman Matloff"
 $ year     : int [1:3] 2023 2019 2011
 $ publisher: chr [1:3] "O'Reilly Media" "CRC Press" "No Starch Press"
 $ isbn     : chr [1:3] "978-1492097402" "978-0815384571" "978-1593273842"

With the files loaded into our environment, we can both compare the two data frames and identify where any differences may be.

identical(html_df, json_df)
[1] FALSE
all.equal(html_df, json_df)
[1] "Component \"authors\": Modes: character, list"              
[2] "Component \"authors\": target is character, current is list"

Taking into account the information we got from the above comparison we can look at the lines that are dissimilar.

print(html_df[[1,2]])
[1] "Hadley Wickham, Mine Çetinkaya-Rundel, Garrett Grolemund"
print(json_df[[1,2]])
[[1]]
[1] "Hadley Wickham"        "Mine Çetinkaya-Rundel" "Garrett Grolemund"    

Now we know that the two tibble cells do not hold that data in the same way. HTML creates what looks like one string while the JSON file creates 3 different strings. The easiest way to get these two dataframes to match is to make the JSON data the same as the HTML file

json_df_flat <- json_df |>
  mutate(authors = map_chr(authors, \(x) paste(x, collapse = ", ")))


identical(html_df, json_df_flat)
[1] TRUE
all.equal(html_df, json_df_flat)
[1] TRUE

Conclusion

I decided to flatten the JSON file even though it is more information and uses the strengths of the tibble format because going forward it is better to treat multiple authors for one book as one discrete datapoint that can be expanded later if needed. The alternative would be to expand the tibble cell in the HTML file.