Search code examples
rxmlxsltxml2

Generate a xml from a R list


I'm new to xml and processing it in R.

I've been able to read and retrieve info from xml files using the xml2 package, but creating xml files from R objects has proven to be more challenging.

In particular, I'd like to generate a xml file from a R list. Consider the example below:

library(reprex)
library(xml2)

r_list <- list(person1 = list(starts = letters[1:3], ends = letters[4:6]), person2 = list(starts = LETTERS[1:4], ends = LETTERS[5:8]))
str(r_list)
#> List of 2
#>  $ person1:List of 2
#>   ..$ starts: chr [1:3] "a" "b" "c"
#>   ..$ ends  : chr [1:3] "d" "e" "f"
#>  $ person2:List of 2
#>   ..$ starts: chr [1:4] "A" "B" "C" "D"
#>   ..$ ends  : chr [1:4] "E" "F" "G" "H"

test1 <- xml2::as_xml_document((r_list))
#> Error: Root nodes must be of length 1

new_xml <- xml_new_root(.value = "category", name = "personList")

for(person in names(r_list)){
  xml_add_child(new_xml, as_xml_document(r_list[person]))
}

new_xml
#> {xml_document}
#> <category name="personList">
#> [1] <person1>ad</person1>
#> [2] <person2>AE</person2>

Created on 2021-11-25 by the reprex package (v2.0.1)

I tried to directly coerce the list to xml using the as_xml_document function, but I get the error Root nodes must be of length 1.

Following the idea on this question, I tried to create the xml document with a root node and xml_add_child() to this document, but I did not get the expected result (see code output). In that question, they transform from an R data frame and not a list.

I'd also like to have personalized tag names and add attributes to these tags. The wished output would be:

<category name="personList">
    <pers name="person1">
        <starts>
            <value>a</value>
            <value>b</value>
            <value>c</value>
        </starts>
        <ends>
            <value>d</value>
            <value>e</value>
            <value>f</value>
        </ends>
    </pers>
    <pers name="person2">
        <starts>
            <value>A</value>
            <value>B</value>
            <value>C</value>
            <value>D</value>
        </starts>
        <ends>
            <value>D</value>
            <value>E</value>
            <value>F</value>
            <value>G</value>
        </ends>
    </pers>
</category>

Thanks for your help and have a nice day


Solution

  • R list attributes can be mapped to XML attributes:

    library(xml2)
    library(tidyverse)
    
    r_list <- list(person1 = list(starts = letters[1:3], ends = letters[4:6]), person2 = list(starts = LETTERS[1:4], ends = LETTERS[5:8]))
    r_list
    
    new_xml <- xml_new_root(.value = "category", name = "personList")
    
    for (person in names(r_list)) {
      p <- list()
      p[["pers"]] <- list(
        starts = r_list[[person]]$starts %>% map(~list(value = list(.x))),
        ends = r_list[[person]]$ends %>% map(~list(value = list(.x)))
      )
      attr(p[["pers"]], "name") <- person
      
      xml_add_child(new_xml, as_xml_document(p))
    }
    
    write_xml(new_xml, "foo.xml")
    

    output:

    <?xml version="1.0" encoding="UTF-8"?>
    <category name="personList">
      <pers name="person1">
        <starts>
          <value>a</value>
          <value>b</value>
          <value>c</value>
        </starts>
        <ends>
          <value>d</value>
          <value>e</value>
          <value>f</value>
        </ends>
      </pers>
      <pers name="person2">
        <starts>
          <value>A</value>
          <value>B</value>
          <value>C</value>
          <value>D</value>
        </starts>
        <ends>
          <value>E</value>
          <value>F</value>
          <value>G</value>
          <value>H</value>
        </ends>
      </pers>
    </category>