【问题标题】:How to parse the information out of XML parent nodes in R如何从R中的XML父节点中解析信息
【发布时间】:2019-07-10 00:43:33
【问题描述】:

我正在使用一个供应商的产品,它以 .udf 文件格式生成一些有趣的 XML 导出。我的目标是能够将 XML 项的层次结构存储在数据框中。

项目是我们在产品内部的文件夹结构中构建的东西。每个项目中的Name 是我们为文件夹指定的名称或我们正在创建的事物的名称(XML 将它们全部称为“项目”,无论它是我们正在创建的事物还是只是一个文件夹)。

这是一个 XML 示例。假设有一个未知的文件夹数,最终会有一个我想要捕获的名称和定义,它设置了我想要在我的数据框中的行数:

<Root>
 <Items>
    <Item Id="2148" Type="Category" Name="Group 1">
        <Item Id="2148" Type="Category" Name="SubGroup A">
            <Item Id="2347" Type="Category" Name="Name1"> [Definition of Name 1] </Item>
            <Item Id="2348" Type="Category" Name="Name2"> [Definition of Name 2] </Item>
        </Item>
        <Item Id="2148" Type="Category" Name="SubGroup A">
            <Item Id="2347" Type="Category" Name="Name1"> [Definition of Name 1] </Item>
            <Item Id="2348" Type="Category" Name="Name2"> [Definition of Name 2] </Item>
        </Item>
    </Item>
</Items>

我希望数据框看起来像这样,其中会有 v1 - vN,具体取决于子文件夹的数量。一个足够好的解决方案是只选择一些子文件夹级别并假设没有更多(不超过 5 个级别,但需要处理更少)。

v1 <- "Group 1"
v2 <- c("SubGroup A", "SubGroup B")
name <- c("Name1", "Name2", "Name 3", "Name4")
definition <- "Definition of name"

df <- tibble::as_tibble(cbind(v1, v2, name, definition))

我可以使用xml2 包为每件事获取一行,其中有一列显示“Root.Items.Item.Item”,但无法从这些父节点中提取信息。

【问题讨论】:

  • 你想把它们全部作为一个数据框吗?
  • 最终是的。我想将此层次结构与另一个层次结构(另一个数据框)进行比较,检查文件夹结构是否相同。

标签: r xml


【解决方案1】:

在某人发布但随后删除的答案的帮助下,我能够找到一个足够好的解决方案(我碰巧看到它,并在他们删除之前复制了他们的代码)。

对我来说,关键是学习 attributes() 函数,它从层次结构较高部分的列表中提取我需要的信息。我的解决方案是手动的,并不优雅,但它确实有效。

下面,显示了我为将数据降低到两个级别所做的工作。我不断地嵌套这些while 循环并调整其中的逻辑,直到 6 层深。假设xml存储在doc中。

xml_ls <- xml2::as_list(xml2::xml_find_all(doc, ".//Item"))

a_len <- length(attributes(xml_ls[[1]])$names)
a_name <- attributes(xml_ls[[1]])$Name
a_id <- attributes(xml_ls[[1]])$Id
a_item <- xml_ls[[1]]$Item

cat_level <- 1
cat_names <- a_name
cat_ids <- a_id
cat_items <- a_item

a_dir <- 1
#print(paste0("Top Level: ", a_name))
while (a_dir <= a_len){
  b_len <- length(attributes(xml_ls[[1]][[a_dir]])$names)
  b_name <- attributes(xml_ls[[1]][[a_dir]])$Name
  b_id <- attributes(xml_ls[[1]][[a_dir]])$Id
  b_item <- xml_ls[[1]][[a_dir]]$Item
  #print(paste0("Level B: ", b_name))

  cat_level <- c(cat_level, 2)
  cat_names <- c(cat_names, b_name)
  cat_ids <- c(cat_ids, b_id)
  cat_items <- c(cat_items, b_item)


  b_dir <- 1
    while (b_dir <= b_len){

      c_len <- length(attributes(xml_ls[[1]][[a_dir]][[b_dir]])$names)
      c_name <- attributes(xml_ls[[1]][[a_dir]][[b_dir]])$Name
      c_id <- attributes(xml_ls[[1]][[a_dir]][[b_dir]])$Id
      c_item <- xml_ls[[1]][[a_dir]][[b_dir]]$Item
      #print(paste0("Level C: ", c_name))

      cat_level <- c(cat_level, 3)
      cat_names <- c(cat_names, c_name)
      cat_ids <- c(cat_ids, c_id)
      cat_items <- c(cat_items, c_item)

这会生成以“cat_”开头的列表。然后我将它们放入一个数据框中,并将其操作成我需要的结构:

df <- as_tibble(cbind(cat_level, cat_ids, cat_names)) %>%
  mutate(cat_level = as.integer(cat_level))

df2 <- df %>%
  mutate(a = if_else(cat_level == 1, cat_names, NA_character_),
         b = if_else(cat_level == 2, cat_names, NA_character_),
         c = if_else(cat_level == 3, cat_names, NA_character_),
         d = if_else(cat_level == 4, cat_names, NA_character_),
         e = if_else(cat_level == 5, cat_names, NA_character_),
         f = if_else(cat_level == 6, cat_names, NA_character_)
         ) %>%
  fill(c(a, b, c, d, e, f)) %>%
  mutate(lvl1 = if_else(cat_level >= 1, a, NA_character_),
         lvl2 = if_else(cat_level >= 2, b, NA_character_),
         lvl3 = if_else(cat_level >= 3, c, NA_character_),
         lvl4 = if_else(cat_level >= 4, d, NA_character_),
         lvl5 = if_else(cat_level >= 5, e, NA_character_),
         lvl6 = if_else(cat_level >= 6, f, NA_character_)) %>%
  select(-a, -b, -c, -d, -e, -f)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 2012-09-28
    • 1970-01-01
    • 2018-12-24
    相关资源
    最近更新 更多