0Pricing
R Academy · Aula

Lidando com estruturas JSON aninhadas

Achate JSON profundamente aninhado em quadros de dados organizados para análise.

Lidando com estruturas JSON aninhadas é uma aula grátis de R Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de R Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de R Academy inclui 4 aulas no total.

Por que o JSON aninhado é difícil

As APIs REST geralmente retornam JSON profundamente aninhado, no qual um único campo pode conter uma matriz de objetos que, por sua vez, contêm mais objetos. Transformar essa estrutura em um quadro de dados organizado exige entender como jsonlite, purrr e tidyr funcionam em conjunto.

# 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() — análise básica

jsonlite::fromJSON() converte uma cadeia JSON ou um caminho de arquivo em objetos do R. Um JSON simples e plano se torna uma lista ou um quadro de dados. Um JSON aninhado se torna uma lista aninhada — matrizes de objetos se tornam quadros de dados armazenados em colunas de lista.

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

O argumento flatten = TRUE instrui fromJSON() a desempacotar recursivamente os quadros de dados aninhados em colunas com nomes separados por pontos. Isso funciona bem para um nível de aninhamento e é a maneira mais rápida de lidar com JSON moderadamente aninhado.

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)

Matrizes aninhadas se tornam colunas de lista

Quando um campo JSON contém uma matriz de objetos, fromJSON() a armazena como uma coluna de lista no quadro de dados — cada célula contém um quadro de dados. Você precisa acessá-las ou desfazer seu aninhamento explicitamente.

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() — extraindo campos aninhados

purrr::map() aplica uma função a cada elemento de uma lista. Quando cada elemento é uma lista nomeada, ou seja, um objeto JSON analisado, você pode passar uma cadeia para extrair um campo nomeado de todos os elementos — uma substituição concisa para um laço.

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() com atalho de cadeia

purrr::map(list, 'field_name') é uma forma abreviada de extrair um campo nomeado de cada elemento — equivalente a map(list, function(x) x[['field_name']]). Use map_chr(), map_dbl() etc. para obter vetores atômicos tipados em vez de listas.

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)

Aninhamento profundo com cadeias de map()

Para JSON profundamente aninhado, encadeie várias chamadas de map(). Cada chamada desce um nível. Use map(list, 'field') em cada nível e aplique um map_*() tipado na etapa mais interna para extrair o valor final.

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() — desfazendo o aninhamento de colunas de lista

tidyr::unnest() expande uma coluna de lista que contém quadros de dados, criando uma linha para cada elemento aninhado. Essa é a abordagem organizada padrão para desfazer o aninhamento de relações um-para-muitos em dados 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() em quadros de dados

jsonlite::flatten() funciona em um quadro de dados já analisado, não em uma cadeia JSON, expandindo recursivamente quaisquer colunas aninhadas de quadros de dados em colunas de nomes separados por pontos. É útil depois de fromJSON(..., flatten = FALSE), quando você quer desfazer o aninhamento como uma etapa de pós-processamento.

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)

Lidando com valores nulos em JSON aninhado

Os valores null do JSON são analisados como NULL no R, o que causa problemas ao criar quadros de dados — um NULL em uma lista elimina o elemento por completo. Use purrr::map() com um argumento .default ou %||% para substituir valores ausentes com segurança.

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

Pipeline completo: de JSON de API a quadro de dados organizado

Juntando tudo: um pipeline realista que analisa JSON aninhado de uma API, extrai campos com purrr, trata valores nulos e produz um quadro de dados organizado, pronto para análise.

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)

Verificação rápida

Você tem um quadro de dados df cuja coluna orders é uma coluna de lista contendo quadros de dados, um por usuário. Qual função expande essa coluna para criar uma linha por pedido?

JSON aninhado — Principais conclusões

O processamento de JSON aninhado em R exige um conjunto de ferramentas em camadas:

  • fromJSON(json, flatten = TRUE) — achata automaticamente um nível de aninhamento
  • fromJSON(json, simplifyDataFrame = FALSE) — obtém uma lista de listas para processamento manual
  • Matrizes aninhadas → colunas de listas no quadro de dados resultante
  • purrr::map_chr/dbl/int(list, 'field') — extrai valores tipados de cada elemento
  • Chamadas encadeadas a map() percorrem níveis profundos de aninhamento
  • tidyr::unnest(df, cols = col) — expande colunas de listas de quadros de dados
  • jsonlite::flatten(df) — achata colunas aninhadas de quadros de dados após a análise
  • Sempre trate NULL com 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)

Perguntas Frequentes

A aula “Lidando com estruturas JSON aninhadas” é grátis?

Sim — o texto completo de “Lidando com estruturas JSON aninhadas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de R Academy, atualize para CoddyKit PRO. O curso de R Academy inclui 4 aulas no total.

O que vou aprender em “Lidando com estruturas JSON aninhadas”?

Achate JSON profundamente aninhado em quadros de dados organizados para análise. Você pratica R Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar R Academy?

Nenhuma experiência prévia é necessária. R Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Lidando com estruturas JSON aninhadas”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de R Academy?

Sim. Cada aula de R Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Analisando JSON com jsonlite
  2. Fazendo solicitações HTTP com httr2
  3. Consumindo APIs REST em R
  4. Lidando com estruturas JSON aninhadas
← Voltar para R Academy