You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从含重复节点名的XML提取数据转为R data.frame的方法

问题:将嵌套XML数据导入R的data.frame

我正尝试将XML文件中的数据导入到R的data.frame中。我对R较为熟悉,但从未接触过XML,因此对此领域感到陌生。

XML示例:

<ArchivedIncident ID="100">
  <attributes>
    <entry>
      <key>TEST1</key>
      <value>
        <type>S</type>
        <value/>
      </value>
    </entry>
    <entry>
      <key>TEST2</key>
      <value>
        <type>S</type>
        <value>12</value>
      </value>
    </entry>
    <entry>
      <key>TEST3</key>
      <value>
        <type>T</type>
        <value>A</value>
      </value>
    </entry>
    <entry>
      <key>TEST4</key>
      <value>
        <type>S</type>
        <value/>
      </value>
    </entry>
  </attributes>
</ArchivedIncident>
<ArchivedIncident ID="101">
  <attributes>
    <entry>
      <key>TEST1</key>
      <value>
        <type>S</type>
        <value>BLAH</value>
      </value>
    </entry>
    <entry>
      <key>TEST2</key>
      <value>
        <type>S</type>
        <value/>
      </value>
    </entry>
    <entry>
      <key>TEST3</key>
      <value>
        <type>T</type>
        <value/>
      </value>
    </entry>
    <entry>
      <key>TEST4</key>
      <value>
        <type>S</type>
        <value/>
      </value>
    </entry>
  </attributes>
</ArchivedIncident>

期望的data.frame结构:

IDTEST1TEST2TEST3TEST4
100NA12ANA
101BLAHNANANA

已完成的操作:

我已经用xml2包读取了ID,代码如下:

require(xml2)
doc <- read_xml("./data/file.xml")
df <- data.frame(
  ID = xml_attr(xml_find_all(doc, ".//ArchivedIncident"), "ID")
)

但我不清楚如何提取剩余数据。XML中存在多个名为entry、value、type的重复节点,我需要提取<key>的文本作为列名,提取对应<key>下的<value>子节点的文本作为值,同时将空值替换为NA。我曾在其他场景使用自定义函数,但不确定是否适用于此,现有自定义函数代码如下:

L <- xml_find_all(doc, ".//ArchivedIncident")
FindAllValues <- function(node){
  tmp <- lapply(L, xml_find_all, paste0(".//", node))
  tmp <- lapply(tmp, xml_text)
  tmp[!sapply(tmp, function(y) length(y == 0))] <- NA
  return(tmp)
}

希望能得到解决方法。


解决方案

我来帮你搞定这个问题,这里有两种实用的方法可以实现你想要的效果,都是基于你已经在使用的xml2包:

方法一:用purrr遍历节点生成数据框

这种方法更简洁,适合处理结构统一的XML,通过遍历每个ArchivedIncident节点,把每个节点转换成一行数据,最后合并成完整的data.frame:

library(xml2)
library(purrr)
library(dplyr)

# 读取XML文档
doc <- read_xml("./data/file.xml")
# 获取所有Incident节点
incidents <- xml_find_all(doc, ".//ArchivedIncident")

# 定义函数处理单个Incident节点
process_incident <- function(incident_node) {
  # 提取ID属性
  id <- xml_attr(incident_node, "ID")
  
  # 获取当前节点下的所有entry
  entries <- xml_find_all(incident_node, "./attributes/entry")
  
  # 提取每个entry的key和对应的value文本
  keys <- xml_text(xml_find_all(entries, "./key"))
  values <- xml_text(xml_find_all(entries, "./value/value"))
  
  # 把空字符串替换成NA(xml_text对空节点会返回空字符串)
  values[values == ""] <- NA
  
  # 组合成一行数据框
  row <- data.frame(ID = id, stringsAsFactors = FALSE)
  row[keys] <- values
  
  return(row)
}

# 处理所有节点并合并成最终数据框
result_df <- map_dfr(incidents, process_incident)

# 查看结果
print(result_df)

运行后就能得到你期望的结构,每个Incident的ID和对应的键值对都正确映射,空值自动转为NA。

方法二:改进自定义函数提取指定key的值

如果你更习惯用循环或者需要单独处理某些key,可以改进你原有的函数,针对性提取每个key对应的值:

library(xml2)

doc <- read_xml("./data/file.xml")
# 获取所有Incident节点
L <- xml_find_all(doc, ".//ArchivedIncident")

# 定义函数:从单个节点中提取指定key的value
get_key_value <- function(node, target_key) {
  # 定位到对应key的entry下的value节点
  value_node <- xml_find_first(node, paste0("./attributes/entry[key='", target_key, "']/value/value"))
  val <- xml_text(value_node)
  # 空值转NA
  if(val == "") NA else val
}

# 获取所有唯一的key,作为数据框的列名
all_keys <- unique(xml_text(xml_find_all(doc, ".//key")))

# 初始化数据框,先放入ID列
df <- data.frame(
  ID = xml_attr(L, "ID"),
  stringsAsFactors = FALSE
)

# 遍历每个key,提取所有节点对应的值并添加为新列
for(key in all_keys) {
  df[[key]] <- sapply(L, get_key_value, target_key = key)
}

# 查看结果
print(df)

这个方法先确定所有需要的列名(唯一的key),然后逐个提取每个key在所有节点中的值,最终组合成完整的数据框,逻辑清晰,也能满足你的需求。

两种方法都能完美实现你想要的效果,你可以根据自己的习惯选择使用~

内容的提问来源于stack exchange,提问作者Wimpel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:39:27