R: R JSON 与 XML

最后更新:2026-08-26

上一课我们学了 CSV/Excel——但 Web 时代的数据越来越多是 JSON 格式(API 响应、配置文件、NoSQL 数据库)。这一课我们学 R 读写 JSON 的标准方案:jsonlite,以及处理 XML 数据的 xml2

读完这一课你就能把 API 返回的嵌套 JSON 5 秒展平成数据框,并写出自己的 JSON 配置文件。

1. 你将学到



2. 一个 API 数据的故事

(1) 痛点:API 返回嵌套 JSON

Bob做天气应用,需要从天气 API 获取数据:

JSON
{
  "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 的解法

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 的"魔法"。

100%
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:什么时候用谁?

100%
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 实例

JSON
{
  "name": "Alice",
  "age": 25,
  "is_student": true,
  "scores": [85, 90, 92],
  "address": {
    "city": "北京",
    "zip": "100000"
  }
}

(3) XML 实例

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>
💡 提示新项目几乎都用 JSON——简洁、易读、解析快。只有老企业系统、政府数据还在用 XML。



4. jsonlite 4 个核心函数

(1) 函数速查表

函数 作用
fromJSON() JSON → R 对象(list / data.frame)
toJSON() R 对象 → JSON 字符串
read_json() 读 JSON 文件/URL
write_json() 写 JSON 文件

(2) fromJSON 详解

R
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 自动展平

R
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
💡 提示:jsonlite 的核心魔法是自动展平嵌套数组为数据框——其他语言(如 Python)要手动 flatten。



5. toJSON 详解

(1) 基本语法

R
toJSON(x, pretty = FALSE, auto_unbox = FALSE, dataframe = "columns")

(2) 实战

R
# 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"
R
# 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 写文件

R
# 写 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 调用

R
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) 批量抓取多用户

R
# 批量获取多个 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) 安装和核心函数

R
install.packages("xml2")
library(xml2)

# 4 个核心函数
read_xml()        # 读 XML 文档
xml_find_all()    # XPath 查询节点
xml_text()        # 提取节点文本
xml_attr()        # 提取节点属性

(2) 实战:读 RSS 订阅

R
# 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 图像)

R
# 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) 项目结构

TEXT 📖 仅展示
project/
├── config.json          # 配置文件
├── R/
│   ├── main.R           # 主程序
│   └── utils.R          # 工具函数
└── data/
    └── input.json       # 输入数据

(2) config.json 示例

JSON
{
  "api": {
    "key": "your-api-key",
    "endpoint": "https://api.example.com",
    "timeout": 30
  },
  "cities": ["北京", "上海", "广州", "深圳"],
  "options": {
    "log_level": "info",
    "max_retries": 3
  }
}

(3) R 程序加载配置

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 数据抓取 + 持久化

R 📖 仅展示
# ============================================
# 天气 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
  }
)
逻辑代码 77 行(超过 40 行限制,仅展示)

预期输出(节选):

TEXT 📖 仅展示
=== 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 多云

❓ 常见问题

Q fromJSON 返回 list 怎么转 data.frame?
A 嵌套数组自动转数据框;纯 list 用 as.data.frame()dplyr::bind_rows() 强转。
Q JSON 里有 null 怎么转 R?
A nullNAtrueTRUEfalseFALSE,数组 → 列表/数据框。jsonlite 自动处理。

📖 小节


📝 作业

  1. 基础题:构造一个嵌套 JSON 字符串(含 name age scores 数组),用 fromJSON() 解析,再用 toJSON(pretty = TRUE) 输出美化版本,验证嵌套数据正确展平。

  2. 基础题:把 1 个数据框(5 行 3 列)写出为 JSON,再用 read_json() 读回,验证数据完整。

  3. 基础题:用 xml2 读 1 个简单 XML 字符串,提取所有 <item> 节点的 name 属性和文本内容,输出数据框。

  4. 进阶题:模拟 1 个 API 响应(含 status data forecast 嵌套结构),用 fromJSON 解析后用 bind_rows 合并 forecast 数组为数据框。

  5. 挑战题:写一个完整程序:① 创建 1 个包含 3 个城市的天气 JSON 文件;② 读取并提取所有城市的当前温度;③ 算出全国平均温度;④ 把结果用 write_json 写回新文件。截图保存控制台输出。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏