Obsługa zagnieżdżonych struktur JSON
Spłaszczaj głęboko zagnieżdżony JSON do uporządkowanych ramek danych na potrzeby analizy.
Obsługa zagnieżdżonych struktur JSON to bezpłatna lekcja R Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej R Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs R Academy zawiera 4 lekcji w sumie.
Dlaczego zagnieżdżony JSON sprawia trudności
Interfejsy REST API często zwracają głęboko zagnieżdżony kod JSON, w którym pojedyncze pole może zawierać tablicę obiektów, a te z kolei mogą zawierać kolejne obiekty. Spłaszczenie takiej struktury do uporządkowanej ramki danych wymaga zrozumienia współdziałania pakietów jsonlite, purrr i tidyr.
# Example nested JSON from a REST API:
json_str <- '{
"user": {
"id": 1,
"name": "Alice",
"orders": [
{"order_id": 101, "total": 59.99, "status": "shipped"},
{"order_id": 102, "total": 24.50, "status": "pending"}
]
}
}'
# The challenge: 'orders' is an array of objects inside 'user'
cat('Nested JSON loaded as string, length:', nchar(json_str))fromJSON() — podstawowe analizowanie
jsonlite::fromJSON() konwertuje ciąg JSON lub ścieżkę do pliku na obiekty R. Prosty, płaski JSON staje się listą lub ramką danych. Zagnieżdżony JSON staje się zagnieżdżoną listą — tablice obiektów są konwertowane na ramki danych przechowywane w kolumnach listowych.
library(jsonlite)
# Parse flat JSON
flat_json <- '{"name": "Alice", "age": 30, "score": 95.5}'
result <- fromJSON(flat_json)
cat('Name:', result$name, '\n')
cat('Age: ', result$age, '\n')
# Parse an array of objects — becomes a data frame
array_json <- '[{"id":1,"val":10},{"id":2,"val":20},{"id":3,"val":30}]'
df <- fromJSON(array_json)
cat('Class:', class(df), '\n')
print(df)fromJSON() z flatten = TRUE
Argument flatten = TRUE informuje funkcję fromJSON(), aby rekurencyjnie rozpakowała zagnieżdżone ramki danych do kolumn o nazwach rozdzielonych kropkami. Działa to dobrze dla jednego poziomu zagnieżdżenia i jest najszybszym sposobem obsługi umiarkowanie zagnieżdżonego JSON-a.
library(jsonlite)
json_str <- '[{
"id": 1,
"name": "Alice",
"address": {"city": "Berlin", "country": "Germany"}
},{
"id": 2,
"name": "Bob",
"address": {"city": "Paris", "country": "France"}
}]'
# Without flatten:
nested_df <- fromJSON(json_str, flatten = FALSE)
cat('address class:', class(nested_df$address), '\n')
# With flatten = TRUE:
flat_df <- fromJSON(json_str, flatten = TRUE)
cat('Columns:', names(flat_df), '\n')
print(flat_df)Zagnieżdżone tablice stają się kolumnami listowymi
Gdy pole JSON zawiera tablicę obiektów, funkcja fromJSON() przechowuje ją jako kolumnę listową w ramce danych — każda komórka zawiera ramkę danych. Należy uzyskać do nich dostęp jawnie lub je rozpakować.
library(jsonlite)
json_str <- '[{
"user_id": 1,
"tags": ["R", "Python", "SQL"]
},{
"user_id": 2,
"tags": ["Java", "Kotlin"]
}]'
df <- fromJSON(json_str)
cat('tags column class:', class(df$tags), '\n')
# Access tags for user 1:
cat('User 1 tags:', df$tags[[1]], '\n')
cat('User 2 tags:', df$tags[[2]])purrr::map() — wyodrębnianie zagnieżdżonych pól
purrr::map() stosuje funkcję do każdego elementu listy. Gdy każdy element jest nazwaną listą (przeanalizowanym obiektem JSON), można przekazać ciąg znaków, aby wyodrębnić nazwane pole ze wszystkich elementów — jest to zwięzły zamiennik pętli.
library(jsonlite)
library(purrr)
json_str <- '[{
"id": 1,
"meta": {"score": 88, "grade": "B"}
},{
"id": 2,
"meta": {"score": 95, "grade": "A"}
},{
"id": 3,
"meta": {"score": 72, "grade": "C"}
}]'
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# Extract 'score' from each record's 'meta' object
scores <- map_dbl(records, function(r) r$meta$score)
grades <- map_chr(records, function(r) r$meta$grade)
cat('Scores:', scores, '\n')
cat('Grades:', grades)purrr::map() ze skróconą składnią dla ciągu znaków
purrr::map(list, 'field_name') to skrócony zapis wyodrębniania nazwanego pola z każdego elementu — odpowiednik map(list, function(x) x[['field_name']]). Użyj map_chr(), map_dbl() itd., aby uzyskać typowane wektory atomowe zamiast list.
library(jsonlite)
library(purrr)
json_str <- '[{"name":"Alice","score":90},{"name":"Bob","score":78},{"name":"Carol","score":85}]'
# Parse as list of lists
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# String shortcut to extract field
names_vec <- map_chr(records, 'name')
scores_vec <- map_dbl(records, 'score')
cat('Names: ', names_vec, '\n')
cat('Scores:', scores_vec, '\n')
# Build a clean data frame
clean_df <- data.frame(name = names_vec, score = scores_vec)
print(clean_df)Głębokie zagnieżdżenie z łańcuchami map()
W przypadku głęboko zagnieżdżonego JSON-a łączysz wiele wywołań map(). Każde wywołanie schodzi o jeden poziom niżej. Na każdym poziomie używaj map(list, 'field'), a na najbardziej wewnętrznym poziomie zastosuj typowane map_*(), aby wyodrębnić końcową wartość.
library(jsonlite)
library(purrr)
json_str <- '[{
"id": 1,
"company": {"hq": {"city": "Berlin", "country": "Germany"}}
},{
"id": 2,
"company": {"hq": {"city": "Tokyo", "country": "Japan"}}
}]'
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# Navigate: records -> company -> hq -> city
cities <- map_chr(records, function(r) r$company$hq$city)
cat('Cities:', cities, '\n')
# Or using nested map shortcut:
ids <- map_int(records, 'id')
cat('IDs:', ids)tidyr::unnest() — spłaszczanie kolumn listowych
tidyr::unnest() rozwija kolumnę listową zawierającą ramki danych, tworząc po jednym wierszu dla każdego zagnieżdżonego elementu. To standardowe podejście tidy do spłaszczania relacji jeden-do-wielu w danych JSON.
library(jsonlite)
library(tidyr)
library(dplyr)
json_str <- '[{
"user_id": 1,
"orders": [{"oid":101,"total":50},{"oid":102,"total":30}]
},{
"user_id": 2,
"orders": [{"oid":103,"total":80}]
}]'
df <- fromJSON(json_str)
cat('Before unnest, rows:', nrow(df), '\n')
cat('orders class:', class(df$orders), '\n')
# Unnest expands one row per order
expanded <- unnest(df, cols = orders)
cat('After unnest, rows:', nrow(expanded), '\n')
print(expanded)jsonlite::flatten() dla ramek danych
jsonlite::flatten() działa na już przeanalizowanej ramce danych (nie na ciągu JSON), rekurencyjnie rozwijając wszystkie zagnieżdżone kolumny zawierające ramki danych do kolumn o nazwach rozdzielonych kropkami. Funkcja jest przydatna po użyciu fromJSON(..., flatten = FALSE), gdy spłaszczanie ma nastąpić na etapie przetwarzania końcowego.
library(jsonlite)
json_str <- '[{
"id": 1,
"profile": {"age": 25, "city": "Rome"}
},{
"id": 2,
"profile": {"age": 31, "city": "Oslo"}
}]'
# Parse without auto-flatten
nested <- fromJSON(json_str, flatten = FALSE)
cat('Columns before flatten:', names(nested), '\n')
cat('profile class:', class(nested$profile), '\n')
# Apply flatten() post-hoc
flat <- flatten(nested)
cat('Columns after flatten:', names(flat), '\n')
print(flat)Obsługa wartości null w zagnieżdżonym JSON-ie
Wartości JSON null są analizowane w R jako NULL, co powoduje problemy podczas tworzenia ramek danych — wartość NULL na liście całkowicie usuwa dany element. Użyj purrr::map() z argumentem .default lub operatora %||%, aby bezpiecznie zastępować brakujące wartości.
library(jsonlite)
library(purrr)
json_str <- '[{"id":1,"email":"alice@example.com"},{"id":2,"email":null},{"id":3,"email":"carol@example.com"}]'
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# Unsafe: NULL drops the element
# emails <- map_chr(records, 'email') # ERROR on null
# Safe: provide a default for missing values
emails <- map_chr(records, function(r) {
if (is.null(r$email)) NA_character_ else r$email
})
cat('Emails:', emails)
cat('NAs:', sum(is.na(emails)))Kompletny potok: od JSON-a z API do uporządkowanej ramki danych
Połączmy wszystkie elementy: realistyczny potok, który analizuje zagnieżdżony JSON z interfejsu API, wyodrębnia pola za pomocą purrr, obsługuje wartości null i tworzy uporządkowaną ramkę danych gotową do analizy.
library(jsonlite)
library(purrr)
library(dplyr)
# Simulated API response
api_json <- '[{
"id": 1, "name": "Alice",
"stats": {"score": 92, "rank": 1}
},{
"id": 2, "name": "Bob",
"stats": null
},{
"id": 3, "name": "Carol",
"stats": {"score": 85, "rank": 3}
}]'
records <- fromJSON(api_json, simplifyDataFrame = FALSE)
result <- tibble(
id = map_int(records, 'id'),
name = map_chr(records, 'name'),
score = map_dbl(records, function(r) if (is.null(r$stats)) NA_real_ else r$stats$score),
rank = map_int(records, function(r) if (is.null(r$stats)) NA_integer_ else r$stats$rank)
)
print(result)Szybki test
Ma Pan/Pani ramkę danych df, w której kolumna orders jest kolumną listową zawierającą ramki danych (po jednej dla każdego użytkownika). Która funkcja rozwija ją do postaci z jednym wierszem na każde zamówienie?
Najważniejsze informacje o zagnieżdżonym JSON
Obsługa zagnieżdżonego JSON w R wymaga zestawu narzędzi działających na różnych poziomach:
fromJSON(json, flatten = TRUE)— automatycznie spłaszcza jeden poziom zagnieżdżeniafromJSON(json, simplifyDataFrame = FALSE)— zwraca listę list do ręcznego przetwarzania- Zagnieżdżone tablice → kolumny listowe w wynikowej ramce danych
purrr::map_chr/dbl/int(list, 'field')— wyodrębnia wartości określonego typu z każdego elementu- Łańcuchy wywołań
map()pozwalają przechodzić przez kolejne poziomy głębokiego zagnieżdżenia tidyr::unnest(df, cols = col)— rozwija kolumny listowe zawierające ramki danychjsonlite::flatten(df)— spłaszcza zagnieżdżone kolumny ramek danych po parsowaniu- Zawsze należy obsługiwać wartość
NULLza pomocąif (is.null(x)) NA else x
library(jsonlite)
library(purrr)
# Quick reference:
json <- '[{"id":1,"info":{"val":10}},{"id":2,"info":null}]'
recs <- fromJSON(json, simplifyDataFrame = FALSE)
# Safe extraction with null guard
vals <- map_dbl(recs, function(r) {
if (is.null(r$info)) NA_real_ else r$info$val
})
result <- data.frame(id = map_int(recs, 'id'), val = vals)
print(result)Często zadawane pytania
Czy lekcja „Obsługa zagnieżdżonych struktur JSON” jest bezpłatna?
Tak — pełny tekst „Obsługa zagnieżdżonych struktur JSON” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu R Academy, przejdź na CoddyKit PRO. Kurs R Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Obsługa zagnieżdżonych struktur JSON”?
Spłaszczaj głęboko zagnieżdżony JSON do uporządkowanych ramek danych na potrzeby analizy. Ćwiczysz R Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć R Academy?
Nie wymagamy żadnego doświadczenia. R Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Obsługa zagnieżdżonych struktur JSON”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji R Academy?
Tak. Każda lekcja R Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Parsowanie JSON za pomocą jsonlite
- Wysyłanie żądań HTTP za pomocą httr2
- Korzystanie z REST API w R
- Obsługa zagnieżdżonych struktur JSON