關卡 1

這門課程要跟各位同學介紹JSON(JavaScriptObjectNotation)資料的處理。

關卡 2

JSON在網際網路上已經是最廣泛使用的格式之一,尤其常見於各種傳遞資料的API之中。舉例來說,台北的Youbike的API資料、甚至是Facebook上的API資料,都是以JSON格式傳輸。

關卡 3

JSON格式也非常簡單,通常我們一般人就可以輕鬆理解。舉例來說,請同學輸入:cat(facebook_error,sep="\n")來看看Facebook的錯誤訊息

cat(facebook_error, sep = "\n")

關卡 4

從畫面上,我們可以簡單看到整個訊息的前後分別有{},這代表整個訊息就是一個JSON物件。每個物件中則包含一對一對的「名稱」與「值」的組合。舉例來說,整個訊息的JSON物件中,只有一對名稱與值得組合:名稱是:“error”,值則是另一個JSON物件。

關卡 5

請問同學,“error”的值中,所包含的一對一對名稱與值的組合中,對應到值為“OAuthException”的名稱,為下列何者?

type

關卡 6

在R中,我們可以利用jsonlite套件,把一個JSON文件轉換成R的list。請同學先安裝jsonlite套件。

check_then_install("jsonlite", "0.9.19")

關卡 7

接著,請載入jsonlite套件。

library(jsonlite)

關卡 8

接著請同學檢查看看jsonlite套件的作者有沒有提供vignette讓R使用者閱讀。請同學輸入:vignette(package="jsonlite")

vignette(package = "jsonlite")

關卡 9

jsonlite的作者很熱情,一口氣寫了四、五篇vignettes。由於我們這次教學的目的是要上手jsonlite物件,所以就請同學輸入:vignette("json-aaquickstart","jsonlite")

vignette("json-aaquickstart", "jsonlite")

關卡 10

這份文件是以網頁的形式,而非pdf的形式呈現給使用者。這種比較不正式,但是更彈性的方式,在近年來受到許多R套件開發者的青睞。例如我本人也寫了一篇這種類型的vignettes。

關卡 11

在Vignettes的第一段很清楚的說明jsonlite的功能:讓R和JSON物件之間能在不損失資訊的狀況下轉換。而作者也馬上給了一個簡單的範例:使用jsonlite提供的toJSONfromJSON,可以完整的還原一個R的data.frame。這種方式,是驗證轉換過程中是否有遺失資訊的一種方式。

關卡 12

範例中所使用的all.equal是一種用來驗證程式碼正確性時,常常使用的工具。它會一字不漏的比對兩個R物件,必需要一模一樣才會回傳TRUE,否則會回報不一致的理由。

關卡 13

文件的第二段描述jsonlite在把JSON物件轉換成R物件時,會自動嘗試把JSON物件轉換成更適合的物件。舉例來說,["Amsterdam","Rotterdam","Utrecht","DenHaag"]如果直接轉換,可能會變成一個長度為4的list,而每個元素都是長度為1的字串向量。

關卡 14

我們已經把["Amsterdam","Rotterdam","Utrecht","DenHaag"]這段文字儲存於變數x1,請同學試試看:fromJSON(x1,simplifyVector=FALSE)

fromJSON(x1, simplifyVector = FALSE)

關卡 15

如前所述,這是一個長度為4的list,而且每個元素都是長度為1的字串向量。

關卡 16

但是如果我們把simplifyVector設定成TRUE,那結果就會不同。這裡因為simplifyVector參數的預設值是TRUE,所以我們不用額外的設定。如果想知道函數的預設值,說明文件中都有清楚的記載。請同學輸入:fromJSON(x1)

fromJSON(x1)

關卡 17

jsonlite的vignettes後面的段落仔細的介紹處理JSON的細節,未來同學如果有需要使用,建議是先把作者提供的vignettes讀過以後,能省掉很多找bug的時間。

關卡 18

jsonlite在某些時候是很方便的套件,所以我們就直接請同學利用它來萃取youbikeAPI所抓下來的資料。請同學在完成之後存檔,並輸入submit()來檢查結果是否符合預期。如果同學在檔案中看到亂碼,請使用Rstudio左上角的File->ReopenWithEncoding…->選取:UTF-8

youbike1 <- fromJSON(youbike_path)

sna1 <- youbike1$result$results$sna
lat1 <- youbike1$result$results$lat
lng1 <- youbike1$result$results$lng
tot1 <- youbike1$result$results$tot
sbi1 <- youbike1$result$results$sbi

rm(youbike1) # 刪除youbike
youbike2 <- fromJSON(youbike_path, simplifyDataFrame = FALSE)
results <- youbike2$result$results
sna2 <- sapply(results, "[[", "sna")
lat2 <- sapply(results, "[[", "lat")
lng2 <- sapply(results, "[[", "lng")
tot2 <- sapply(results, "[[", "tot")
sbi2 <- sapply(results, "[[", "sbi")

answer <- data.frame(stringsAsFactors = FALSE,
  sna = sna2,
  lat = as.numeric(lat2),
  lng = as.numeric(lng2),
  tot = as.integer(tot2),
  sbi = as.integer(sbi2)
)

stopifnot(nrow(answer) == 10)
stopifnot(isTRUE(all.equal(sum(answer$lat), 250.35549511)))
stopifnot(isTRUE(all.equal(sum(answer$lng), 1215.65644412)))
stopifnot(sum(answer$tot) == 702)
stopifnot(sum(answer$sbi) == 0)