Skip to contents

xmlwriter is an R package that provides a fast and simple interface for creating XML documents and fragments from R. It has a simple elegant syntax for creating xml_fragments. xmlwriter's XML generation from R lists is fast, implemented in C++ using Rcpp.

Details

xmlwriter can be used as a companion to R packages XML or xml2 which are both wonderful packages optimized for parsing, querying and manipulating XML documents. Both XML and xml2 provide several ways for creating XML documents, but they are not optimized for generating and writing XML.

Creating XML documents with XML and xml2 can be a bit cumbersome, mostly because it forces the author to manipulate the XML document tree, obscuring the XML structure of the document, and making it hard to read the XML that is being generated. xml2 does provide a way to create XML documents from R data structures using nested lists which is a powerful feature, but it is not optimized for speed or readability.

xmlwriter provides an intuitive interface for creating XML documents, that mimics how XML is written in a text editor. It has two different ways to create XML documents:

  • a light weight R syntax using tag(), frag(), +, / and data_frag(), creating an xml_fragment(), that can be easily translated into a character or xml2::xml_document object, or be used as a flexible building block for generating a larger XML document.

  • an xmlbuilder() object that allows you to create XML documents in a feed-forward manner, with start and end methods, giving you more control on the XML document structure, including XML comment, prolog etc.

It implements several xml2 methods:

  • as_xml_document.xml_fragment()

  • as_list.xml_fragment()

  • write_xml.xml_fragment()

Author

Maintainer: Edwin de Jonge edwindjonge@gmail.com (ORCID)

Examples

doc <- xml_fragment(
  study = frag(
    .attr = c(id="1"),
    person = frag(
      .attr = c(id = "p1"),
      name = "John Doe",
      age = 30
    ),
    person = frag(
      name = "Jane Doe",
      age = 25,
      address = frag(street = "123 Main St", city = "Springfield"),
      "This is a text node"
    )
  )
)

print(doc)
#> {xml_fragment}
#> <study id="1">
#>   <person id="p1">
#>     <name>John Doe</name>
#>     <age>30</age>
#>   ... 
if (require("xml2")){
  as_xml_document(doc)
}
#> {xml_document}
#> <study id="1">
#> [1] <person id="p1">\n  <name>John Doe</name>\n  <age>30</age>\n</person>
#> [2] <person><name>Jane Doe</name><age>25</age><address><street>123 Main St</s ...

# you can create a function to generate an xml fragment:
person_frag <- function(name, age, id){
  tag("person", id = id) / frag(
    name = name,
    age  = age,
    address = frag(
      street = "123 Main St",
      city = "Springfield"
    )
  )
}

# xml_doc is a xml_fragment with the restriction of having one root element
doc2 <- xml_doc("study") / (
  person_frag("John Doe", 30, "p1") +
  person_frag("Jane Doe", 25, "p2")
)

print(doc2)
#> {xml_doc,xml_fragment}
#> <?xml version='1.0' encoding='UTF-8'?>
#>  <study>
#>   <person id="p1">
#>     <name>Joh... 

if (require("xml2")){
  as_xml_document(doc2)
}
#> {xml_document}
#> <study>
#> [1] <person id="p1">\n  <name>John Doe</name>\n  <age>30</age>\n  <address>\n ...
#> [2] <person id="p2">\n  <name>Jane Doe</name>\n  <age>25</age>\n  <address>\n ...

# a fragment can have multiple root elements
fgmt <- person_frag("John Doe", 30, id = "p1") +
  person_frag("Jane Doe", 25, id = "p2")


print(fgmt)
#> {xml_fragment (2)}
#> [1]<person id="p1">
#>   <name>John Doe</name>
#>   <age>30</age>
#>   <address>
#>     <street...
#> [2]<person id="p2">
#>   <name>Jane Doe</name>
#>   <age>25</age>
#>   <address>
#>     <street...
#> ...

if (require("xml2")){
  # as_xml_document won't work because it expects a single root element,
  # so we retrieve a nodeset instead
  as_xml_nodeset(fgmt)
}
#> {xml_nodeset (2)}
#> [1] <person id="p1">\n  <name>John Doe</name>\n  <age>30</age>\n  <address>\n ...
#> [2] <person id="p2">\n  <name>Jane Doe</name>\n  <age>25</age>\n  <address>\n ...

iris_xml <- xml_doc("fieldstudy", id = "iris", doi ="10.1111/j.1469-1809.1936.tb02137.x") /
  frag(
    source = "Fisher, R. A. (1936) The use of multiple measurements in
taxonomic problems. Annals of Eugenics, 7, Part II, 179–188.",
    data = data_frag(iris, row_tag = "obs")
  )

print(iris_xml, max_characters = 300)
#> {xml_doc,xml_fragment}
#> <?xml version='1.0' encoding='UTF-8'?>
#>  <fieldstudy id="iris" doi="10.1111/j.1469-1809.1936.tb02137.x">
#>   <source>Fisher, R. A. (1936) The use of multiple measurements in
#> taxonomic problems. Annals of Eugenics, 7, Part II, 179–188.</source>
#>   <data>
#>     <obs>
#>       <Sepal.Length>5.1</Sepal.Length>
#>  ... 

if (require("xml2")){
  as_xml_document(iris_xml)
}
#> {xml_document}
#> <fieldstudy id="iris" doi="10.1111/j.1469-1809.1936.tb02137.x">
#> [1] <source>Fisher, R. A. (1936) The use of multiple measurements in\ntaxonom ...
#> [2] <data>\n  <obs>\n    <Sepal.Length>5.1</Sepal.Length>\n    <Sepal.Width>3 ...