用xml2包读取XML最稳妥,需显式指定encoding参数防乱码,用xml_find_all()定位重复节点并逐个提取,避免as_list()丢失边界,提取后及时类型转换和缺失值处理。

直接用 xml2 包读取 XML,再配合 xml2::as_list() 或 xml2::xml_find_all() 提取节点,是最稳妥的起点。R 原生的 XML 包已逐渐被 xml2 取代,后者更轻量、API 更一致,且对中文和特殊字符支持更好。
用 xml2::read_xml() 加载文件,别跳过编码检查
XML 文件若含中文或 UTF-8 BOM,read_xml() 默认可能解析失败或乱码。必须显式指定 encoding 参数:
library(xml2)
doc <- read_xml("data.xml", encoding = "UTF-8")常见错误现象:xml2::read_xml() 报错 Input is not proper UTF-8,或中文字段显示为 符号。此时先用系统命令确认编码:
- Linux/macOS:运行
file -i data.xml - Windows:用 Notepad++ 查看“编码”菜单栏
- 不确定时,可尝试
encoding = "UTF-8"或encoding = "GBK"
用 xml2::xml_find_all() 定位重复结构节点
XML 中最常遇到的是多组同名子节点(如多个 ),需用 XPath 定位后逐个提取。不要依赖 as_list() 直接转嵌套列表——它会丢失重复节点的边界,导致数据错位。
假设 XML 结构如下:
Alice 95 Bob 87
正确做法是:
records <- xml_find_all(doc, "//record") df <- data.frame( id = xml_attr(records, "id"), name = xml_text(xml_find_first(records, ".//name")), score = as.numeric(xml_text(xml_find_first(records, ".//score"))) )
关键点:
-
.//name表示在当前record节点内查找任意层级的name,避免写死路径 -
xml_attr()提取属性值,xml_text()提取文本内容 - 若某字段可能为空,用
xml_text(..., trim = TRUE)并配合ifelse(is.na(...), NA, ...)防止强制转换出错
映射字段时注意类型与缺失值处理
XML 本身无类型,所有值都是字符串。直接转 data.frame 后,数值列可能是 character 类型,后续计算会报错。
推荐在提取阶段就做类型转换,并统一处理空值:
score_nodes <- xml_find_all(records, ".//score") scores <- xml_text(score_nodes) scores[scores == ""] <- NA_character_ df$score <- as.numeric(scores)
容易踩的坑:
-
as.numeric("NA")得到NaN,不是NA;应先替换空字符串为"NA"再转,或用readr::parse_number() - 日期字段(如
)要用2023-05-12 as.Date()显式解析,不能留作字符 - 布尔字段(如
active="true")需用xml_attr() %in% c("true", "1")转逻辑值
真正麻烦的不是读取,而是当 XML 混合了属性、文本、嵌套子节点,且部分记录字段缺失时,XPath 表达式稍有偏差就会漏数据或错行。建议先用 xml_structure(doc) 快速看树形结构,再小范围测试单个 xml_find_all() 调用,确认返回节点数量和顺序符合预期,再批量提取。










