R Academy · Lektion

Hantera nästlade JSON-strukturer

Platta ut djupt nästad JSON till tidy data frames för analys.

Lektion 4 av 413 steg

Hantera nästlade JSON-strukturer är en gratis lektion i R Academy på CoddyKit. Detta är lektion 4 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för R Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i R Academy innehåller totalt 4 lektioner.

Varför nästlad JSON är svårhanterlig

REST-API:er returnerar ofta djupt nästlad JSON där ett enskilt fält kan innehålla en array med objekt, som i sin tur innehåller fler objekt. För att platta ut denna struktur till en tidy data frame behöver man förstå hur jsonlite, purrr och tidyr samverkar.

# 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() — Grundläggande tolkning

jsonlite::fromJSON() konverterar en JSON-sträng eller filsökväg till R-objekt. Enkel platt JSON blir en lista eller data frame. Nästlad JSON blir en nästlad lista — arrayer med objekt blir data frames som lagras i listkolumner.

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() med flatten = TRUE

Argumentet flatten = TRUE instruerar fromJSON() att rekursivt packa upp nästlade data frames till kolumner med punktseparerade namn. Detta fungerar bra för en nästlingsnivå och är det snabbaste sättet att hantera måttligt nästlad JSON.

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)

Nästlade arrayer blir listkolumner

När ett JSON-fält innehåller en array med objekt lagrar fromJSON() den som en listkolumn i data framen — varje cell innehåller en data frame. Dessa måste nås eller packas upp uttryckligen.

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() — Extrahera nästlade fält

purrr::map() tillämpar en funktion på varje element i en lista. När varje element är en namngiven lista, alltså ett tolkat JSON-objekt, kan en sträng skickas in för att extrahera ett namngivet fält från varje element — ett kortfattat alternativ till en loop.

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() med strängförkortning

purrr::map(list, 'field_name') är en kortform för att extrahera ett namngivet fält från varje element — motsvarande map(list, function(x) x[['field_name']]). Använd map_chr(), map_dbl() och så vidare för att få typade atomära vektorer i stället för listor.

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)

Djup nästling med kedjor av map()

För djupt nästlad JSON kedjar man flera anrop till map(). Varje anrop går ner en nivå. Använd map(list, 'field') på varje nivå och tillämpa ett typat map_*() på den innersta nivån för att extrahera det slutliga värdet.

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() — Platta ut listkolumner

tidyr::unnest() expanderar en listkolumn som innehåller data frames och skapar en rad för varje nästlat element. Detta är tidy-standardmetoden för att platta ut en-till-många-relationer i JSON-data.

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() på data frames

jsonlite::flatten() fungerar på en redan tolkad data frame, inte på en JSON-sträng, och expanderar rekursivt alla nästlade kolumner med data frames till kolumner med punktseparerade namn. Det är användbart efter fromJSON(..., flatten = FALSE) när man vill platta ut datan som ett efterbearbetningssteg.

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)

Hantera null-värden i nästlad JSON

JSON-värdet null tolkas som NULL i R, vilket orsakar problem när data frames byggs — ett NULL i en lista tar bort elementet helt. Använd purrr::map() med argumentet .default eller %||% för att ersätta saknade värden på ett säkert sätt.

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)))

Fullständig pipeline: från API-JSON till tidy data frame

Här sätts allt ihop: en realistisk pipeline som tolkar nästlad JSON från ett API, extraherar fält med purrr, hanterar null-värden och skapar en tidy data frame som är klar för analys.

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)

Snabbtest

Ni har en data frame df där kolumnen orders är en listkolumn som innehåller data frames, en per användare. Vilken funktion expanderar denna till en rad per order?

Viktigaste slutsatserna om nästlad JSON

Hantering av nästlad JSON i R kräver en verktygslåda i flera lager:

  • fromJSON(json, flatten = TRUE) — plattar automatiskt ut en nivå av nästling
  • fromJSON(json, simplifyDataFrame = FALSE) — ger en lista av listor för manuell bearbetning
  • Nästlade arrayer → listkolumner i den resulterande dataramen
  • purrr::map_chr/dbl/int(list, 'field') — extraherar typade värden från varje element
  • Kedjade anrop till map() går igenom djupt nästlade nivåer
  • tidyr::unnest(df, cols = col) — expanderar listkolumner som innehåller dataramar
  • jsonlite::flatten(df) — plattar ut nästlade dataramkolumner efter parsning
  • Hantera alltid NULL med 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)
Gratis att börja

Lär dig R med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
43
Lektioner
159

Vanliga frågor

Är lektionen ”Hantera nästlade JSON-strukturer” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen R Academy, inklusive ”Hantera nästlade JSON-strukturer”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i R Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Hantera nästlade JSON-strukturer”?

Platta ut djupt nästad JSON till tidy data frames för analys. Ni övar på R Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig R Academy?

Du behöver inga förkunskaper. Utbildningen i R Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”Hantera nästlade JSON-strukturer”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här R Academy-lektionen?

Ja. Varje R Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Tolka JSON med jsonlite
  2. Skicka HTTP-förfrågningar med httr2
  3. Anropa REST-API:er i R
  4. Hantera nästlade JSON-strukturer
← Tillbaka till R Academy