R Academy · Les

Geneste JSON-structuren verwerken

Maak diep geneste JSON-structuren plat tot tidy dataframes voor analyse.

Les 4 van 413 stappen

Geneste JSON-structuren verwerken is een gratis R Academy-les op CoddyKit. Dit is les 4 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject R Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus R Academy bevat in totaal 4 lessen.

Waarom geneste JSON lastig is

REST-API's retourneren vaak diep geneste JSON waarin één veld een array met objecten kan bevatten, die op hun beurt weer meer objecten bevatten. Om deze structuur om te zetten naar een overzichtelijk gegevensframe moet je begrijpen hoe jsonlite, purrr en tidyr samenwerken.

# 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() — basisparsering

jsonlite::fromJSON() zet een JSON-tekenreeks of een bestandspad om naar R-objecten. Eenvoudige vlakke JSON wordt een lijst of gegevensframe. Geneste JSON wordt een geneste lijst — arrays met objecten worden gegevensframes die in lijstkolommen zijn opgeslagen.

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

Het argument flatten = TRUE vertelt fromJSON() dat geneste gegevensframes recursief moeten worden uitgepakt naar kolommen met namen die door punten worden gescheiden. Dit werkt goed voor één niveau van nesting en is de snelste manier om JSON met een gemiddelde mate van nesting te verwerken.

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)

Geneste arrays worden lijstkolommen

Wanneer een JSON-veld een array met objecten bevat, slaat fromJSON() dit op als een lijstkolom in het gegevensframe — elke cel bevat een gegevensframe. Je moet deze expliciet benaderen of ont nesten.

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() — geneste velden ophalen

purrr::map() past een functie toe op elk element van een lijst. Wanneer elk element een lijst met namen is (een geparseerd JSON-object), kun je een tekenreeks doorgeven om uit elk element een veld met die naam op te halen — een beknopt alternatief voor een lus.

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() met een snelkoppeling voor tekenreeksen

purrr::map(list, 'field_name') is een verkorte manier om uit elk element een veld met een bepaalde naam op te halen — gelijkwaardig aan map(list, function(x) x[['field_name']]). Gebruik map_chr(), map_dbl() enzovoort om getypeerde atomische vectoren te krijgen in plaats van lijsten.

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)

Diepe nesting met map()-ketens

Bij diep geneste JSON koppel je meerdere aanroepen van map() aan elkaar. Elke aanroep daalt één niveau af. Gebruik op elk niveau map(list, 'field') en pas bij de binnenste stap een getypeerde map_*() toe om de uiteindelijke waarde op te halen.

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() — lijstkolommen vlak maken

tidyr::unnest() vouwt een lijstkolom met gegevensframes uit en maakt één rij per genest element. Dit is de standaardaanpak van tidy voor het vlak maken van één-op-veelrelaties in JSON-gegevens.

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() op gegevensframes

jsonlite::flatten() werkt op een al geparseerd gegevensframe (niet op een JSON-tekenreeks) en breidt geneste kolommen met gegevensframes recursief uit naar kolommen met namen die door punten worden gescheiden. Dit is nuttig na fromJSON(..., flatten = FALSE) wanneer je het gegevensframe als nabewerking vlak wilt maken.

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)

Null-waarden in geneste JSON verwerken

JSON-waarden null worden in R geparseerd als NULL, wat problemen veroorzaakt bij het maken van gegevensframes — een NULL in een lijst verwijdert het element volledig. Gebruik purrr::map() met een argument .default of %||% om ontbrekende waarden veilig te vervangen.

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

Volledige pijplijn: van API-JSON naar een overzichtelijk gegevensframe

Alles samenbrengen: een realistische pijplijn die geneste JSON van een API parseert, velden met purrr ophaalt, null-waarden verwerkt en een overzichtelijk gegevensframe oplevert dat klaar is voor analyse.

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)

Korte controle

Je hebt een gegevensframe df waarin de kolom orders een lijstkolom is met gegevensframes (één per gebruiker). Welke functie vouwt dit uit naar één rij per bestelling?

Geneste JSON — belangrijkste punten

Voor het verwerken van geneste JSON in R heb je een toolkit met meerdere lagen nodig:

  • fromJSON(json, flatten = TRUE) — maakt automatisch één niveau van nesting plat
  • fromJSON(json, simplifyDataFrame = FALSE) — levert een lijst met lijsten voor handmatige verwerking
  • Geneste arrays → lijstkolommen in het resulterende gegevensframe
  • purrr::map_chr/dbl/int(list, 'field') — haalt waarden van een specifiek type uit elk element
  • Opeenvolgende map()-aanroepen dalen af door diepe nestingniveaus
  • tidyr::unnest(df, cols = col) — vouwt lijstkolommen met gegevensframes uit
  • jsonlite::flatten(df) — maakt geneste kolommen met gegevensframes na het parseren plat
  • Vang NULL altijd af met 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 beginnen

Leer R met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
43
Lessen
159

Veelgestelde vragen

Is de les “Geneste JSON-structuren verwerken” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad R Academy, waaronder “Geneste JSON-structuren verwerken”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus R Academy bevat in totaal 4 lessen.

Wat leer ik in “Geneste JSON-structuren verwerken”?

Maak diep geneste JSON-structuren plat tot tidy dataframes voor analyse. Je oefent met R Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met R Academy te beginnen?

Ervaring vooraf is niet nodig. R Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 4 van 4.

Hoe lang duurt de les “Geneste JSON-structuren verwerken”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over R Academy?

Ja. Elke les over R Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. JSON parseren met jsonlite
  2. HTTP-verzoeken uitvoeren met httr2
  3. REST-API's gebruiken in R
  4. Geneste JSON-structuren verwerken
← Terug naar R Academy