将xml-object写入磁盘

时间:2017-05-19 12:48:59

标签: r xml readr

我有一大堆xml - 文件,我需要处理它们。就此而言,我希望能够读取文件,并将生成的对象列表保存到磁盘。我试图用readr::write_rds保存列表,但在再次读取它之后,该对象有所修改,并且不再有效。我能做些什么来缓解这个问题吗?

library(readr)
library(xml2)

x <- read_xml("<foo>
              <bar>text <baz id = 'a' /></bar>
              <bar>2</bar>
              <baz id = 'b' />
              </foo>")

# function to save and read object
roundtrip <- function(obj) {
  tf <- tempfile()
  on.exit(unlink(tf))

  write_rds(obj, tf)
  read_rds(tf)
}

list(x)
#> [[1]]
#> {xml_document}
#> <foo>
#> [1] <bar>text <baz id="a"/></bar>
#> [2] <bar>2</bar>
#> [3] <baz id="b"/>
roundtrip(list(x))
#> [[1]]
#> {xml_document}

identical(x, roundtrip(x))
#> [1] FALSE
all.equal(x, roundtrip(x))
#> [1] TRUE
xml_children(roundtrip(x))
#> Error in fun(x$node, ...): external pointer is not valid
as_list(roundtrip(x))
#> Error in fun(x$node, ...): external pointer is not valid

一些上下文

我有大约500,000个xml文件。为了处理它们,我计划将它们变成一个带有xml2::as_list的列表,然后我编写代码来提取我需要的东西。后来我意识到,as_list运行起来非常昂贵。我可以:

  1. 重新编写已经仔细调试的代码以直接解析数据(xml_childxml_text,...)或
  2. 使用as_list
  3. 为了加快没有。 2我可以在另一台具有更多内核的计算机上运行它,但我想将一个文件传递给该计算机,因为收集和复制所有文件非常耗时。

1 个答案:

答案 0 :(得分:4)

xml2 对象的外部指针在您天真地序列化时会变得无效。该软件包提供xml_serialize()xml_unserialize()个对象来为您处理此问题。不幸的是,API有点麻烦,因为base::serialize()base::unserialize()假定是开放连接。


library(xml2)

x <- read_xml("<foo>
              <bar>text <baz id = 'a' /></bar>
              <bar>2</bar>
              <baz id = 'b' />
              </foo>")

# function to save and read object
roundtrip <- function(obj) {
  tf <- tempfile()
  con <- file(tf, "wb")
  on.exit(unlink(tf))

  xml_serialize(obj, con)
  close(con)
  con <- file(tf, "rb")
  on.exit(close(con), add = TRUE)
  xml_unserialize(con)
}
x
#> {xml_document}
#> <foo>
#> [1] <bar>text <baz id="a"/></bar>
#> [2] <bar>2</bar>
#> [3] <baz id="b"/>
(y <- roundtrip(x))
#> {xml_document}
#> <foo>
#> [1] <bar>text <baz id="a"/></bar>
#> [2] <bar>2</bar>
#> [3] <baz id="b"/>

identical(x, y)
#> [1] FALSE
all.equal(x, y)
#> [1] TRUE
xml_children(y)
#> {xml_nodeset (3)}
#> [1] <bar>text <baz id="a"/></bar>
#> [2] <bar>2</bar>
#> [3] <baz id="b"/>
as_list(y)
#> $bar
#> $bar[[1]]
#> [1] "text "
#> 
#> $bar$baz
#> list()
#> attr(,"id")
#> [1] "a"
#> 
#> 
#> $bar
#> $bar[[1]]
#> [1] "2"
#> 
#> 
#> $baz
#> list()
#> attr(,"id")
#> [1] "b"

同样关于你问题的第二部分,我会认真考虑使用XPATH表达式来提取所需的数据,即使你必须重写代码。