R语言递归解析xml2嵌套列表 提取全层级节点属性
嵌套XML列表属性提取问题
提取xml2::as_list()输出的多层嵌套列表所有节点属性时,固定层数的循环逻辑无法适配不同XML文件的嵌套深度,需要通用递归遍历方案。
测试数据
以下为复现问题的样例列表结构,和真实XML转换后的结构完全一致:
identifier = list( "000000001" ) attr(identifier, ",scheme") <- "http://example.com" member <- list( "A Member" ) attr(member, ",dimension") <- "MemberAxis" segment <- list( Imember = member, Imember = member, Imember = member ) attr(segment, "$names") <- c("member", "member", "member") node_start = list( identifier = identifier, segment = segment ) attr(node_start, "$names") <- c("identifier","member") # 需要解析的嵌套列表对象 # 注:原示例此处存在笔误,自引用会触发无限递归,实际应为已定义的node_start对象 xml_list <- list( node_start = node_start ) # 常规处理:跳过首个无属性包装节点 node_start <- xml_list[[1]]
已尝试方案的问题
rapply方案
先后尝试过两段rapply遍历逻辑:
# 尝试1 node_recursion <- rapply(node_start, attributes, how = "list", classes = "list") # 尝试2 node_recursion <- rapply(node_start, function(y){ message(y) attributes(y)[[1]] }, how = "list", classes = "list")
该方案缺陷:rapply只会递归处理最底层的非列表节点,无法回溯获取父层级节点的属性,输出结果不全。
自定义递归循环尝试
先后写过两版自定义递归函数,均存在逻辑错误:
# 版本1 node_recursion <- function(node_start){ n <- 1 l <- length(node_start) for (i in 1:l) { if(is.list(node_start[[l]])){ attributes(node_start[[l]]) n <- n + 1 message("added n") }else{ message("hit return") return(node_recursion(node_start[[l]][[n]])) } } } node_recursion(node_start)
# 版本2(适配深层数据尝试) node_recursion <- function(node, l){ for (i in 1:l) { s <- node_attribute[[1]][[x]][[1]][[i]] z <- 1 while (is.list(s)) { tryCatch( expr = { n <- attributes(s[[z]]) %>% as.data.frame() z <- z + 1 }, error = function(e){ message(paste("layer", z, "break")) break }) } } }
该类方案缺陷:循环索引逻辑混乱,存在递归终止条件错误、索引越界、未遍历全部分支的问题,无法稳定运行。
临时可用方案
目前临时通过反复unlist的方式提取属性,虽然能拿到结果但实现不规范:
attributes_list <- list() z <- 1 for (i in node_start) { nested_attributes <- lapply(node_start, attributes) node_start <- unlist(node_start, recursive = FALSE) attributes_list[[z]] <- nested_attributes z <- z + 1 }
该方案缺陷:执行过程会反复修改原始输入对象,鲁棒性差,遇到特殊嵌套结构容易出现属性遗漏。
期望输出
需要得到保留属性名称的扁平化属性列表,格式参考:
list( scheme = "http://example.com", dimension = "MemberAxis", dimension = "MemberAxis", dimension = "MemberAxis" )
规范递归实现
递归逻辑核心:处理每个节点时先提取当前节点属性,再遍历所有子节点递归提取,最后将所有层级的属性拼接为扁平化列表,同时自动清洗xml2转换生成的特殊属性标记:
extract_all_xml_attrs <- function(node) { result <- list() # 提取当前节点属性,过滤R列表自带的names内部属性 current_attr <- attributes(node) current_attr[["names"]] <- NULL # 清洗xml2生成的属性名特殊前缀(逗号、$符号) if (length(current_attr) > 0) { names(current_attr) <- gsub("^[,\\$]", "", names(current_attr)) result <- c(result, current_attr) } # 遍历所有子节点,递归提取属性 for (child in node) { if (is.list(child)) { child_attr <- extract_all_xml_attrs(child) result <- c(result, child_attr) } } return(result) } # 调用方式 all_attrs <- extract_all_xml_attrs(xml_list[[1]])
该实现的特点:
- 适配任意嵌套深度,不需要提前预知XML节点层数
- 自动过滤列表结构自带的内部属性,清洗后属性名和XML原始属性名一致
- 执行过程不修改原始输入对象,无副作用
- 扩展性强,需要记录节点层级、节点路径时只需增加一个层级参数传递即可
内容的提问来源于stack exchange,提问作者S3AN556
相关产品推荐
相关产品推荐

