我正在利用供应商的产品,该产品以.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”,但无法从那些父节点中提取信息。
答案 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)