2

If I have an XMLNode object that looks like this:

<foo>
 <a>1</a>
 <b>
  <c>1</c>
  <d>2</d>
 </b>
</foo>

(available as dput dump: https://gist.github.com/4273470)

without knowing the nestedness of the "c" node, how can I change its value from "1" to "2"? I have been playing with the XML package, but it is not terribly clear - most examples appear to deal with extracting rather than updating information.

I have also tried using xmlToList, but then I don't know how to identify / update a node on a list of arbitrary depth either.

Abe
  • 12,956
  • 12
  • 51
  • 72
  • To find node of arbitrary depth in a list see this nice example using a recursive function: http://stackoverflow.com/a/26154446/199217 – David LeBauer Oct 02 '14 at 04:59

2 Answers2

3

Here's a two-step method. (test is the dput output you included).

Find the path to $c

> xmlApply(test,names)
$a
  text 
"text" 

$b
  c   d 
"c" "d" 

Replace $c once you know the path

xmlChildren(test[["b"]][["c"]]) <- "2"
Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
  • thanks ... any thoughts on how to automatically parse the output of `xmlApply(test, names)` for use in the second step? (something easier than starting with `do.call(paste("xmlChildren(test[['",...`) – Abe Dec 13 '12 at 02:39
2

You can (probably, almost) always come up with a brute-force combination of loops and conditionals, but the exact structure and complexity of this will depend on your assumptions.

For example, assuming that there is a known maximum depth of 2 (parent nodes = c("a", "b") and child nodes = c("c", "d") as in the example, plus a known list of candidate target child nodes (say, "c" and "d"), but unknown parent-child relationships, you could do this:

replacement.list <- list(c = 2)

test.list <- xmlToList(test)
parent.names <- names(test.list)
for(replacement.name in names(replacement.list)){
  for(parent.name in parent.names){
    child.names <- names(test.list[[parent.name]])
    if(replacement.name %in% child.names){
      xmlChildren(test[[parent.name]][[replacement.name]]) <- replacement.list[[replacement.name]]
    }
  }
}

Update: a more elegant approach using recursive function is here: https://stackoverflow.com/a/26154446/199217

Community
  • 1
  • 1
David LeBauer
  • 31,011
  • 31
  • 115
  • 189