R: R JSON 与 XML
最后更新:2026-08-26
上一课我们学了 CSV/Excel——但 Web 时代的数据越来越多是 JSON 格式(API 响应、配置文件、NoSQL 数据库)。这一课我们学 R 读写 JSON 的标准方案:
jsonlite,以及处理 XML 数据的xml2。
读完这一课你就能把 API 返回的嵌套 JSON 5 秒展平成数据框,并写出自己的 JSON 配置文件。
1. 你将学到
- JSON 与 XML 是什么,什么时候用
- jsonlite 包安装与 4 个核心函数
- fromJSON 读取 JSON(嵌套展平)
- toJSON 输出 JSON
- 嵌套 JSON 实战(API 响应)
- xml2 读取 XML 文件
- JSON ↔ data.frame 互转
- 实战:API 数据批量抓取
2. 一个 API 数据的故事
(1) 痛点:API 返回嵌套 JSON
Bob做天气应用,需要从天气 API 获取数据:
{
"status": "ok",
"data": {
"city": "北京",
"date": "2024-01-15",
"forecast": [
{"day": "周一", "high": 5, "low": -3, "weather": "晴"},
{"day": "周二", "high": 7, "low": -1, "weather": "多云"},
{"day": "周三", "high": 3, "low": -5, "weather": "雪"}
]
}
}
他想提取"7 天天气预报"存到 R 里分析。Python 写 requests + json 10 行;R 一行搞定——
(2) R 的解法
library(jsonlite)
# 1. 一行代码读取嵌套 JSON
weather <- fromJSON("https://api.weather.com/forecast?city=北京")
# 2. 提取 7 天预报(嵌套 → 数据框)
forecast <- weather$data$forecast # 直接是数据框!
# 3. 写出 JSON 配置
config <- list(api_key = "xxx", cities = c("北京", "上海"))
write_json(config, "config.json", pretty = TRUE)
3 行代码搞定 API 嵌套 JSON。这就是 jsonlite 的"魔法"。
graph LR
A[API URL<br/>weather.com/forecast] --> B[jsonlite::fromJSON]
B --> C{嵌套 JSON}
C --> D[forecast 数组<br/>自动转 data.frame]
C --> E[current 对象<br/>自动转 list]
D --> F[dplyr 分析<br/>7 天预报]
D --> G[ggplot2 画图<br/>趋势可视化]
E --> H[实时天气数据]
style A fill:#cce5ff
style B fill:#d4edda
style C fill:#fff3cd
style D fill:#f8d7da
style E fill:#e1d4ff
style F fill:#ffe1d4
style G fill:#cce5ff
style H fill:#d4edda
3. JSON vs XML:什么时候用谁?
graph TB
subgraph JSON[JSON 阵营]
J1[语法: 键值对 简洁]
J2[类型: 数字/字符串/布尔/数组/对象]
J3[解析: 快 C 语言实现]
J4[R 包: jsonlite]
end
subgraph XML[XML 阵营]
X1[语法: 标签嵌套 冗长]
X2[类型: 全是字符串]
X3[解析: 较慢]
X4[R 包: xml2]
end
JSON --> J5[Web API / NoSQL / 配置文件]
XML --> X5[老企业系统 / SOAP / RSS / SVG]
style JSON fill:#d4edda
style XML fill:#f8d7da
(1) 两种格式对比
| 特性 | JSON | XML |
|---|---|---|
| 全称 | JavaScript Object Notation | eXtensible Markup Language |
| 语法 | {"key": "value"} |
<key>value</key> |
| 可读性 | 简洁 | 冗长 |
| 数据类型 | 数字、字符串、布尔、null、数组、对象 | 全是字符串 |
| 解析速度 | 快 | 较慢 |
| 主要场景 | Web API、NoSQL、配置文件 | 老企业系统、SOAP、文档(RSS、SVG) |
| R 包 | jsonlite |
xml2 |
(2) JSON 实例
{
"name": "Alice",
"age": 25,
"is_student": true,
"scores": [85, 90, 92],
"address": {
"city": "北京",
"zip": "100000"
}
}
(3) XML 实例
<student>
<name>Alice</name>
<age>25</age>
<scores>
<item>85</item>
<item>90</item>
<item>92</item>
</scores>
<address>
<city>北京</city>
<zip>100000</zip>
</address>
</student>
4. jsonlite 4 个核心函数
(1) 函数速查表
| 函数 | 作用 |
|---|---|
fromJSON() |
JSON → R 对象(list / data.frame) |
toJSON() |
R 对象 → JSON 字符串 |
read_json() |
读 JSON 文件/URL |
write_json() |
写 JSON 文件 |
(2) fromJSON 详解
library(jsonlite)
# 1. 读取 JSON 字符串
json_str <- '{"name": "Alice", "age": 25}'
fromJSON(json_str)
# $name
# [1] "Alice"
#
# $age
# [1] 25
# 2. 读取 JSON 文件
df <- fromJSON("data.json")
# 3. 读取 URL(API)
weather <- fromJSON("https://api.example.com/weather?city=beijing")
# 4. 直接展开为数据框
json_array <- '[{"name": "A", "age": 20}, {"name": "B", "age": 25}]'
fromJSON(json_array)
# name age
# 1 A 20
# 2 B 25 ← 自动转数据框!
(3) 嵌套 JSON 自动展平
nested <- '{
"status": "ok",
"data": {
"city": "北京",
"forecast": [
{"day": "周一", "high": 5, "low": -3},
{"day": "周二", "high": 7, "low": -1}
]
}
}'
result <- fromJSON(nested)
str(result)
# List of 2
# $ status: chr "ok"
# $ data : List of 2
# ..$ city : chr "北京"
# ..$ forecast :'data.frame': 2 obs. of 3 variables:
# .. ..$ day : chr [1:2] "周一" "周二"
# .. ..$ high: num [1:2] 5 7
# .. ..$ low : num [1:2] -3 -1
# 嵌套数组自动转数据框!
result$data$forecast
# day high low
# 1 周一 5 -3
# 2 周二 7 -1
5. toJSON 详解
(1) 基本语法
toJSON(x, pretty = FALSE, auto_unbox = FALSE, dataframe = "columns")
(2) 实战
# 1. 数据框 → JSON
df <- data.frame(name = c("A", "B"), age = c(20, 25))
toJSON(df)
# [{"name":"A","age":20},{"name":"B","age":25}]
# 2. 美化输出(带缩进)
toJSON(df, pretty = TRUE)
# [
# {
# "name": "A",
# "age": 20
# },
# {
# "name": "B",
# "age": 25
# }
# ]
# 3. 列表 → JSON
config <- list(
api_key = "secret123",
cities = c("北京", "上海", "广州"),
options = list(timeout = 30, retry = 3)
)
toJSON(config, pretty = TRUE)
# {
# "api_key": "secret123",
# "cities": ["北京", "上海", "广州"],
# "options": {
# "timeout": 30,
# "retry": 3
# }
# }
(3) 关键参数
| 参数 | 作用 | 默认 |
|---|---|---|
pretty |
美化(带缩进) | FALSE |
auto_unbox |
长度 1 的向量自动去掉数组 | FALSE |
dataframe |
数据框展开方式 | "columns" |
# auto_unbox:让单个值不变成数组
toJSON(list(name = "Alice", scores = c(85))) # 默认
# {"name":["Alice"],"scores":[85]}
toJSON(list(name = "Alice", scores = c(85)), auto_unbox = TRUE)
# {"name":"Alice","scores":85} ← 单值不数组化
(4) write_json 写文件
# 写 JSON 文件
write_json(config, "config.json", pretty = TRUE)
# 写紧凑 JSON(不缩进)
write_json(data, "data.json")
# 追加到文件(先 read 再合并再 write)
old <- read_json("data.json", simplifyVector = FALSE)
new <- c(old, list(updated_at = Sys.time()))
write_json(new, "data.json", pretty = TRUE)
6. 实战:API 数据批量抓取
(1) 真实 API 调用
library(jsonlite)
library(dplyr)
# 调用一个公共 API(GitHub 用户信息)
user_info <- fromJSON("https://api.github.com/users/hadley")
str(user_info)
# List of 50+
# $ login : chr "hadley"
# $ id : int 4192
# $ name : chr "Hadley Wickham"
# $ company : chr "@posit-pbc"
# $ location : chr "Houston, TX"
# $ public_repos : int 50
# ...
# 提取关键字段
info <- tibble(
name = user_info$name,
company = user_info$company,
repos = user_info$public_repos,
followers = user_info$followers
)
print(info)
(2) 批量抓取多用户
# 批量获取多个 GitHub 用户的仓库数
users <- c("hadley", "yihui", "jtleek", "rstudio")
repos_data <- lapply(users, function(user) {
info <- fromJSON(paste0("https://api.github.com/users/", user))
tibble(
user = user,
repos = info$public_repos,
followers = info$followers,
created_at = as.Date(info$created_at)
)
}) |> bind_rows()
print(repos_data)
7. xml2:读 XML 文件
(1) 安装和核心函数
install.packages("xml2")
library(xml2)
# 4 个核心函数
read_xml() # 读 XML 文档
xml_find_all() # XPath 查询节点
xml_text() # 提取节点文本
xml_attr() # 提取节点属性
(2) 实战:读 RSS 订阅
# 1. 读 RSS(XML 格式)
rss <- read_xml("https://www.r-bloggers.com/feed")
# 2. 提取所有 <item> 节点
items <- xml_find_all(rss, "//item")
cat("找到", length(items), "篇文章\n")
# 3. 提取每篇文章的标题、链接、日期
articles <- tibble(
title = xml_text(xml_find_all(items, "./title")),
link = xml_attr(xml_find_all(items, "./link"), "href"),
pub_date = xml_text(xml_find_all(items, "./pubDate"))
)
print(head(articles, 3))
(3) 实战:读 SVG(XML 图像)
# SVG 是 XML 格式
svg <- read_xml("logo.svg")
# 提取所有 <circle> 的 cx, cy, r 属性
circles <- xml_find_all(svg, "//circle")
data.frame(
cx = as.numeric(xml_attr(circles, "cx")),
cy = as.numeric(xml_attr(circles, "cy")),
r = as.numeric(xml_attr(circles, "r"))
)
8. 实战:JSON 配置文件读写
(1) 项目结构
project/
├── config.json # 配置文件
├── R/
│ ├── main.R # 主程序
│ └── utils.R # 工具函数
└── data/
└── input.json # 输入数据
(2) config.json 示例
{
"api": {
"key": "your-api-key",
"endpoint": "https://api.example.com",
"timeout": 30
},
"cities": ["北京", "上海", "广州", "深圳"],
"options": {
"log_level": "info",
"max_retries": 3
}
}
(3) R 程序加载配置
# 加载配置
config <- read_json("config.json", simplifyVector = FALSE)
print(config$cities)
# [1] "北京" "上海" "广州" "深圳"
# 使用配置
for (city in config$cities) {
url <- paste0(config$api$endpoint, "?city=", city, "&key=", config$api$key)
data <- fromJSON(url)
# 处理 data...
}
# 更新配置
config$options$log_level <- "debug"
write_json(config, "config.json", pretty = TRUE, auto_unbox = TRUE)
9. 完整示例:API 数据抓取 + JSON 持久化
下面是一个完整工作流示例,把本课所有知识串起来。
▶ 示例:天气 API 数据抓取 + 持久化
# ============================================
# 天气 API 数据抓取 + 持久化
# 功能:模拟抓取 4 城市天气,存为 JSON
# ============================================
library(jsonlite)
library(dplyr)
# 1. 模拟 API 响应(实际项目用 fromJSON(url))
mock_api_response <- function(city) {
set.seed(match(city, c("北京", "上海", "广州", "深圳")))
list(
status = "ok",
data = list(
city = city,
date = Sys.Date(),
current = list(
temp = sample(0:30, 1),
humidity = sample(40:90, 1),
weather = sample(c("晴", "多云", "雨", "雪"), 1)
),
forecast = data.frame(
day = c("今天", "明天", "后天", "大后天"),
high = sample(5:30, 4),
low = sample(-5:15, 4),
weather = sample(c("晴", "多云", "雨", "雪"), 4, replace = TRUE),
stringsAsFactors = FALSE
)
)
)
}
# 2. 批量抓取 4 城市
cities <- c("北京", "上海", "广州", "深圳")
cat("=== 抓取 4 城市天气 ===\n")
all_weather <- lapply(cities, mock_api_response)
names(all_weather) <- cities
# 3. 提取所有预报(嵌套 → 数据框)
all_forecast <- bind_rows(lapply(all_weather, function(w) {
fc <- w$data$forecast
fc$city <- w$data$city
fc
}))
cat("\n=== 4 城市未来 4 天预报 ===\n")
print(all_forecast)
# 4. 提取当前天气(嵌套 → 数据框)
current_weather <- bind_rows(lapply(all_weather, function(w) {
cw <- w$data$current
tibble(
city = w$data$city,
temp = cw$temp,
humidity = cw$humidity,
weather = cw$weather
)
}))
cat("\n=== 当前天气 ===\n")
print(current_weather)
# 5. 保存为 JSON
write_json(all_weather, "all_weather.json", pretty = TRUE, auto_unbox = TRUE)
cat("\n=== 已保存 all_weather.json ===\n")
# 6. 保存为压缩 JSON(去除缩进)
write_json(all_weather, "all_weather_compact.json", auto_unbox = TRUE)
# 7. 读取回 JSON 验证
reloaded <- read_json("all_weather.json", simplifyVector = FALSE)
cat("\n=== 读回验证 ===\n")
cat("城市数:", length(reloaded), "\n")
cat("北京当前温度:", reloaded$北京$data$current$temp, "°C\n")
# 8. 写出单城市精简 JSON 配置
config <- list(
api_key = "secret-key-xxx",
default_city = "北京",
update_interval = 3600,
enabled_cities = cities
)
write_json(config, "config.json", pretty = TRUE, auto_unbox = TRUE)
cat("\n=== 配置文件已生成 config.json ===\n")
# 9. 读取配置文件
loaded_config <- read_json("config.json", simplifyVector = FALSE)
cat("默认城市:", loaded_config$default_city, "\n")
cat("启用城市:", paste(loaded_config$enabled_cities, collapse = ", "), "\n")
# 10. 用 jsonlite 处理 API 错误
cat("\n=== 错误处理示例 ===\n")
error_response <- '{"status": "error", "message": "Invalid API key"}'
result <- tryCatch(
{
parsed <- fromJSON(error_response)
if (parsed$status == "error") stop(parsed$message)
parsed
},
error = function(e) {
cat("API 错误:", e$message, "\n")
NULL
}
)
预期输出(节选):
=== 4 城市未来 4 天预报 ===
day high low weather city
1 今天 12 -3 晴 北京
2 明天 15 0 多云 北京
3 后天 8 -5 雪 北京
...
=== 当前天气 ===
# A tibble: 4 × 4
city temp humidity weather
<chr> <int> <int> <chr>
1 北京 18 65 晴
2 上海 22 78 多云
3 广州 28 85 雨
4 深圳 26 72 多云
❓ 常见问题
as.data.frame() 或 dplyr::bind_rows() 强转。null 怎么转 R?null → NA,true → TRUE,false → FALSE,数组 → 列表/数据框。jsonlite 自动处理。📖 小节
- JSON 是 Web API 标准格式,XML 在老企业系统/RSS/SVG 还在用
- jsonlite 是 R 处理 JSON 的标准:纯 R 依赖、自动展平嵌套数据、性能好
- 4 个核心函数:
fromJSON/toJSON/read_json/write_json - jsonlite 魔法:嵌套数组自动转 data.frame(其他语言要手动 flatten)
toJSON()关键参数:pretty(美化)、auto_unbox(单值不数组化)- xml2 读 XML:4 个函数
read_xmlxml_find_allxml_textxml_attr,配合 XPath - JSON 适合存配置文件(人可读 + 通用);R 内部用
saveRDS(保留类型)
📝 作业
-
基础题:构造一个嵌套 JSON 字符串(含
nameagescores数组),用fromJSON()解析,再用toJSON(pretty = TRUE)输出美化版本,验证嵌套数据正确展平。 -
基础题:把 1 个数据框(5 行 3 列)写出为 JSON,再用
read_json()读回,验证数据完整。 -
基础题:用
xml2读 1 个简单 XML 字符串,提取所有<item>节点的name属性和文本内容,输出数据框。 -
进阶题:模拟 1 个 API 响应(含
statusdataforecast嵌套结构),用fromJSON解析后用bind_rows合并forecast数组为数据框。 -
挑战题:写一个完整程序:① 创建 1 个包含 3 个城市的天气 JSON 文件;② 读取并提取所有城市的当前温度;③ 算出全国平均温度;④ 把结果用
write_json写回新文件。截图保存控制台输出。