如何从R中的XML父节点解析信息

时间:2019-07-05 15:57:51

标签: r xml

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

这些项目是我们在产品内部的文件夹结构中构建的。每个项目中的Name是我们给该文件夹的名称,或者是我们正在创建的事物的名称(无论是我们正在创建的事物还是一个文件夹,XML都将它们称为“ Item”) 。

这是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”,但无法从那些父节点中提取信息。

1 个答案:

答案 0 :(得分:0)

我能够找到一个足够好的解决方案,部分原因是有人发布但后来删除了答案(我碰巧看到了,并在删除之前复制了他们的代码)。

对我来说,关键是学习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)