HTML and JSON: Code Base Deliverable

Author

Chanice McKenzie

1 Planned approach

For this assignment, I plan to explore how the same information can be stored in HTML and JSON and then imported into R. I will use three books on the subject of data science and statistics. This topic connects to my coursework and provides an opportunity to practice organizing a small dataset before working with larger files from websites and APIs.

I will select three published books related to data science, statistics, or data analysis, making sure at least one has multiple authors. For each book, I will record its title, all authors, publication year, publisher, and ISBN. I will verify the bibliographic details using publisher or catalog information before creating the files.

I will use the same three books, the same field names, and the same values in both formats. For books with multiple authors, I plan to keep the authors together in a consistently ordered, semicolon-separated text field. This will make it easier to compare the imported tables, while still preserving each author’s name.

Once both files are available, I plan to use rvest to read the HTML table into an R data frame and jsonlite to read the JSON records into a separate data frame. I will use dplyr as needed to select and arrange columns consistently and standardize text fields and data types.

I will inspect the column names, dimensions, and sample records from both imported data frames. Then I will compare their structures and values using an appropriate R comparison function, such as identical() after standardizing the column order and types. If they do not match, I will examine the differences and trace them back to the source files or import process rather than assuming the files are equivalent.

2 Anticipated Data Challenges

The first challenge will be representing multiple authors consistently. JSON can naturally store an array of author names, while a basic HTML table cell usually contains text. To keep the comparison straightforward, I will use the same semicolon-separated representation in both source files and document that choice.

A second challenge will be data types. Publication years may be read as numbers in one format but as text in the other, and ISBNs should remain character strings because they are identifiers rather than quantities. I will explicitly check and align types before comparing the data frames.

A third challenge will be differences in spacing, punctuation, special characters, or column order. Small inconsistencies can cause a comparison to fail even when the records appear similar. I will preserve consistent values during manual entry and document any cleaning needed after import.

3 Expected Outcome and Next Steps

My goal is to produce two equivalent data frames from two different source formats and clearly report whether they are identical after any documented standardization.

4 Introduction

For this assignment, I explore how the same information can be stored in HTML and JSON and imported into R. I selected three published books on data science and statistics, including two with multiple authors, and recorded each title, author list, publication year, publisher, and ISBN. The two source files are publicly hosted as books.html and books.json.

5 Source Files and Publication

The original manually authored source files are books.html and books.json. I published both through GitHub Pages. The analysis reads these public URLs, rather than local file paths. The report must have internet access when it renders.

# Public GitHub Pages links supplied for this assignment.
html_url <- "https://chanicemcken.github.io/Data-607-Week-7-HTML-and-JSON/books.html"
json_url <- "https://chanicemcken.github.io/Data-607-Week-7-HTML-and-JSON/books.json"

knitr::kable(
  data.frame(format = c("HTML", "JSON"), url = c(html_url, json_url)),
  caption = "Publicly accessible source data"
)
Publicly accessible source data
format url
HTML https://chanicemcken.github.io/Data-607-Week-7-HTML-and-JSON/books.html
JSON https://chanicemcken.github.io/Data-607-Week-7-HTML-and-JSON/books.json

6 Data Import and Inspection

I use rvest::read_html() and html_table() to extract the HTML table, and jsonlite::fromJSON() to read the JSON records. The two imports are stored separately so the comparison is not influenced by shared objects.

html_doc <- tryCatch(rvest::read_html(html_url), error = function(e) {
  stop("Cannot access books.html at ", html_url,
       ". Commit the file to the public GitHub repository first. Details: ", conditionMessage(e))
})
html_tables <- rvest::html_elements(html_doc, "table#books")
if (length(html_tables) != 1L) stop("Expected exactly one table with id='books'.")
books_from_html <- rvest::html_table(html_tables[[1]], convert = FALSE)
knitr::kable(books_from_html, caption = "Book data imported from HTML")
Book data imported from HTML
title authors publication_year publisher isbn
R for Data Science (2nd Edition) Hadley Wickham; Mine Çetinkaya-Rundel; Garrett Grolemund 2023 O’Reilly Media 9781492097402
Python for Data Analysis (3rd Edition) Wes McKinney 2022 O’Reilly Media 9781098104030
Practical Statistics for Data Scientists (2nd Edition) Peter Bruce; Andrew Bruce; Peter Gedeck 2020 O’Reilly Media 9781492072942
books_from_json <- tryCatch(jsonlite::fromJSON(json_url, simplifyDataFrame = TRUE),
                            error = function(e) {
  stop("Cannot access books.json at ", json_url,
       ". Commit the file to the public GitHub repository first. Details: ", conditionMessage(e))
})
books_from_json <- tibble::as_tibble(books_from_json)
knitr::kable(books_from_json, caption = "Book data imported from JSON")
Book data imported from JSON
title authors publication_year publisher isbn
R for Data Science (2nd Edition) Hadley Wickham; Mine Çetinkaya-Rundel; Garrett Grolemund 2023 O’Reilly Media 9781492097402
Python for Data Analysis (3rd Edition) Wes McKinney 2022 O’Reilly Media 9781098104030
Practical Statistics for Data Scientists (2nd Edition) Peter Bruce; Andrew Bruce; Peter Gedeck 2020 O’Reilly Media 9781492072942

7 Standardization and Comparison

I standardize the same five columns in the same order and explicitly convert the year to integer and the ISBN to character. This prevents superficial type differences from being mistaken for content differences. I do not remove or invent records, and I preserve the semicolon-separated author order.

fields <- c("title", "authors", "publication_year", "publisher", "isbn")
normalize_books <- function(data) {
  if (!all(fields %in% names(data))) {
    stop("Missing expected fields: ", paste(setdiff(fields, names(data)), collapse = ", "))
  }
  data |>
    dplyr::select(dplyr::all_of(fields)) |>
    dplyr::mutate(
      title = trimws(as.character(title)),
      authors = trimws(as.character(authors)),
      publication_year = as.integer(publication_year),
      publisher = trimws(as.character(publisher)),
      isbn = trimws(as.character(isbn))
    ) |>
    as.data.frame(stringsAsFactors = FALSE)
}

html_df <- normalize_books(books_from_html) |>
  dplyr::arrange(title) |>
  as.data.frame(stringsAsFactors = FALSE)
json_df <- normalize_books(books_from_json) |>
  dplyr::arrange(title) |>
  as.data.frame(stringsAsFactors = FALSE)

# Rows are sorted by title in both imported data frames so the comparison tests book contents rather than accidental differences in source row order.
identical_result <- identical(html_df, json_df)
comparison <- data.frame(
  Check = c("HTML rows", "JSON rows", "Same column names",
            "Same column types", "Identical data frames"),
  Result = c(nrow(html_df), nrow(json_df),
             identical(names(html_df), names(json_df)),
             identical(lapply(html_df, class), lapply(json_df, class)),
             identical_result)
)
knitr::kable(comparison, caption = "Comparison of the two imported data frames")
Comparison of the two imported data frames
Check Result
HTML rows 3
JSON rows 3
Same column names 1
Same column types 1
Identical data frames 1
if (!identical_result) {
  cat("The imported data frames differ. Inspect the following comparison:\n")
  print(all.equal(html_df, json_df))
}

8 Conclusions

The HTML import contains 3 books, and the JSON import contains 3 books. After aligning column order and data types, identical() returned TRUE. This confirms that the two manually created source formats represent the same data under the documented normalization rules.