在R中调用getSibling()从XML文件中提取单个节点会导致崩溃

时间:2017-07-30 17:13:03

标签: r xml xml-parsing

我试图使用R脚本从一个非常大的(~620 MB)XML文件中一次提取一个节点。我想要访问的每个主节点对应于不同的药物,并且所有节点彼此平行。我的目标是一次处理整个文件,一个节点,因为尝试将整个文件读入内存不适用于R中的XML解析器。

我已经将我的大型XML文件显着截断为一个只包含4个节点的小得多的示例文件;这个XML文件的开头如下:

<?xml version="1.0" encoding="UTF-8"?>
<drugbank xmlns="http://www.drugbank.ca" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.drugbank.ca http://www.drugbank.ca/docs/drugbank.xsd" version="5.0" exported-on="2017-07-06">
<drug type="biotech" created="2005-06-13" updated="2016-08-17">
  <drugbank-id primary="true">DB00001</drugbank-id>
  <drugbank-id>BTD00024</drugbank-id>
  <drugbank-id>BIOD00024</drugbank-id>
  <name>Lepirudin</name>
  <description>Lepirudin is identical to natural hirudin except for substitution of leucine for isoleucine at the N-terminal end of the molecule and the absence of a sulfate group on the tyrosine at position 63. It is produced via yeast cells. Bayer ceased the production of lepirudin (Refludan) effective May 31, 2012.</description>
  <cas-number>138068-37-8</cas-number>
  <unii>Y43GF64R34</unii>
  <state>liquid</state>
  <groups>
    <group>approved</group>
  </groups>

审查了可用的选项,并希望使用我已编写的R脚本从XML文件中提取所需的字段(它适用于小型XML文件,但对于大文件而言失败),似乎使用了R中的XML库中的getSibling()函数是我的最佳选择。以下示例代码(来自http://svitsrv25.epfl.ch/R-doc/library/XML/html/addSibling.html)用于提取此示例文件中的单个节点:

f = system.file("exampleData", "job.xml", package = "XML")
tt = as(xmlParse(f), "XMLHashTree")
x = xmlRoot(tt, skip = FALSE)
DesiredOutput <- getSibling(x)
# I’m still not sure how to “walk” to the next sibling after the above process completes, since this example file only contains one node, and there is no simple way to increment a counter using the above code

该示例job.xml文件开头如下:

<!-- Initial Comment -->
<gjob:Helping xmlns:gjob="http://www.gnome.org/some-location">
  <gjob:Jobs>

    <gjob:Job>
      <gjob:Project ID="3"/>
      <gjob:Application>GBackup</gjob:Application>
      <gjob:Category>Development</gjob:Category>

      <gjob:Update>
        <gjob:Status>Open</gjob:Status>
        <gjob:Modified>Mon, 07 Jun 1999 20:27:45 -0400 MET DST</gjob:Modified>
        <gjob:Salary>USD 0.00</gjob:Salary>
      </gjob:Update>

      <gjob:Developers>
        <gjob:Developer>
        </gjob:Developer>
      </gjob:Developers>

但是,如果我替换自己的XML文件(完整文件的小版本;我已检查它是合法的XML格式,因为我的R脚本正确运行以处理它),以下代码崩溃R:

f = "MyTruncatedExampleFile.xml" -> this line causes R to crash
tt = as(xmlParse(f), "XMLHashTree")
x = xmlRoot(tt, skip = FALSE)
DesiredOutput <- getSibling(x)

有人可以建议为什么我自己的小XML文件会导致崩溃,但示例job.xml文件是否正确运行?提前感谢您的见解。

1 个答案:

答案 0 :(得分:0)

显然,这似乎是由于截断的XML中未声明的名称空间前缀导致崩溃:xmlns="http://www.drugbank.ca"。如果删除它,该方法不会崩溃R.请注意:未声明的命名空间在XML文档中有效。因此,应该使用XML包作者提出此问题。此外,由于<drug>在截断的XML中没有兄弟,因此下面使用xmlChildren()代替getSibling()

# CRASHES
f = "DrugBank.xml"
tt = as(xmlParse(f), "XMLHashTree")
x = xmlRoot(tt, skip = FALSE)
DesiredOutput <- xmlChildren(x)[[1]]
DesiredOutput

# NO CRASHES
f = "DrugBank_oth.xml"                      # REMOVED UNDEFINED NAMESPACE PREFIX
tt = as(xmlParse(f), "XMLHashTree")
x = xmlRoot(tt, skip = FALSE)
DesiredOutput <- xmlChildren(x)[[1]]
DesiredOutput

作为一种不修改原始XML的解决方法,请考虑getNodeSet,在其中为特殊命名空间定义前缀,并将XPath定义到根{hidh /*/*的子级别。索引[[1]]用于获取第一个节点而不是所有节点。这里, web 用作前缀,但它可以是任何内容。

# NO CRASHES
f = "DrugBank.xml"
doc = xmlParse(f)
nmsp = c(web="http://www.drugbank.ca")      # DEFINE NAMESPACE PREFIX

DesiredOutput <- getNodeSet(doc, "/*/*", nmsp)[[1]]
DesiredOutput

# <drug type="biotech" created="2005-06-13" updated="2016-08-17">
#   <drugbank-id primary="true">DB00001</drugbank-id>
#   <drugbank-id>BTD00024</drugbank-id>
#   <drugbank-id>BIOD00024</drugbank-id>
#   <name>Lepirudin</name>
#   <description>Lepirudin is identical to natural hirudin except for 
#                substitution of leucine for isoleucine at the N-terminal 
#                end of the molecule and the absence of a sulfate group on 
#                the tyrosine at position 63. It is produced via yeast 
#                cells. Bayer ceased the production of lepirudin (Refludan) 
#                effective May 31, 2012.</description>
#   <cas-number>138068-37-8</cas-number>
#   <unii>Y43GF64R34</unii>
#   <state>liquid</state>
#   <groups>
#     <group>approved</group>
#   </groups>
# </drug>