【问题标题】:R: parse large unstructured xml fileR:解析大型非结构化 xml 文件
【发布时间】:2018-10-13 00:50:11
【问题描述】:

我有一个非常复杂的 xml 文件需要在 R 中解析并以数据框格式呈现。结构可能类似于以下示例。节点不平行。

<Root>
  <A>
   <info1>a</info1>
     <child>
       <info2>b</info2>
       <info3>c</info3>
       <info4>d</info4>
     </child>
   <info5>e</info5>
  </A>
  <B>
   <info6>f</info6>
   <info7>g</info7>
  </B>
</Root>

我想出了一些代码来解析文件:

doc <- xmlParse(file="sample.xml", useInternal = TRUE)
rootnode <- xmlRoot(doc)
df1<-xmlToDataFrame(nodes=getNodeSet(rootnode, "//Root/A"))
df2<-xmlToDataFrame(nodes=getNodeSet(rootnode, "//Root/B"))
Final<-cbind.data.frame(df1,df2, all=TRUE)

返回结果为:(所有的值表单节点都收缩在一起)

info1 child info5 info6 info7
  a    bcd    e     f     g

但是,我想要的理想结果是:

info1 info2 info3 info4 info5 info6 info7
  a     b     c     d     e     f     g

由于xml文件中有大量节点,类似上面的情况,手动操作dataframe是不明智的。
我也尝试将路径语句改为“//Root/A/child”,那么节点A和节点B下的所有值都会丢失。 有没有人可以提供这个问题的解决方案。提前致谢。

【问题讨论】:

    标签: r xml parsing


    【解决方案1】:

    可以尝试xmlToListunlist 以减少命名矢量格式的xml 数据。可以使用 gsub 更改名称以符合 OP 的期望:

    library(XML)
    result <- unlist(xmlToList(xmlParse(xml)))
    #Change the name to refer only child 
    names(result) <- gsub(".*\\.(\\w+)$","\\1", names(result))
    result 
    # info1 info2 info3 info4 info5 info6 info7 
    # "a"   "b"   "c"   "d"   "e"   "f"   "g"
    

    数据:

    xml <- "<Root>
      <A>
      <info1>a</info1>
      <child>
      <info2>b</info2>
      <info3>c</info3>
      <info4>d</info4>
      </child>
      <info5>e</info5>
      </A>
      <B>
      <info6>f</info6>
      <info7>g</info7>
      </B>
      </Root>"
    

    【讨论】:

    • 非常感谢MKR,根据你的代码,我直接data.frame到unlist结果。我得到的数据框只有来自所有节点的值,但没有列名(节点名)。你知道我可以以某种方式添加所有节点名称以及所有值吗?非常感谢。
    【解决方案2】:

    在结构较少的 XML 中,最好执行以下操作:

    library(XML)
    Final <- data.frame(xmlToList(rootnode), recursive = T, use.names = T)
    

    如果您不喜欢自动设置的列名,您可以简单地执行use.names = F 并设置您自己的名称。

    【讨论】:

    • 非常感谢 Kim,我尝试了代码,但是,它给了我错误消息:参数意味着不同的行数:1、0。
    【解决方案3】:

    使用starts-with()匹配节点

    > doc = xmlParse(xml)
    > xpathSApply(doc, "//*[starts-with(name(), 'info')]", xmlValue)
    [1] "a" "b" "c" "d" "e" "f" "g"
    > xpathSApply(doc, "//*[starts-with(name(), 'info')]", xmlName)
    [1] "info1" "info2" "info3" "info4" "info5" "info6" "info7"
    

    所以

    query <- "//*[starts-with(name(), 'info')]"
    setNames(
        xpathSApply(doc, query, xmlValue),
        xpathSApply(doc, query, xmlName)
    )
    

    【讨论】:

    • 非常感谢 Martin,xml 文件只是一个示例,我的实际 xml 文件比这更复杂,每个节点名称都与其他节点名称完全不同。因此,使用startswith函数无法完成任务。不过谢谢你的建议。
    猜你喜欢
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    • 2015-08-26
    • 1970-01-01
    相关资源
    最近更新 更多