Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating XML in R with namespaces

Tags:

namespaces

r

xml

I'm trying to build an XML structure in R that looks like this. Note the default namespace, the other namespaces, and the inner Name object which is in an alternate namespace:

<?xml version = "1.0" encoding="UTF-8"?>
<sld xmlns="http://www.o.net/sld" 
     xmlns:ogc="http://www.o.net/ogc" 
     xmlns:se="http://www.o.net/se"
     version="1.1.0" >
<layer>
<se:Name>My Layer</se:Name>
</layer>
</sld>

This code comes close, having the namespaces on the tags okay, but no default namespace in the root tag:

require(XML)
makesimple = function(){
    z = xmlTree("sld",
                namespaces=list(
                    "ogc"="http://www.o.net/ogc",
                    "se"="http://www.o.net/se"),
                attrs=list(version="1.1.0")
                )
    z$addNode("layer", close=FALSE)
    z$setNamespace("se")
    z$addNode("Name","My Layer")
    z$closeTag()
    z
}
> cat(saveXML(makesimple()))

gives

<?xml version="1.0"?>
 <sld 
  xmlns:ogc="http://www.o.net/ogc"
  xmlns:se="http://www.o.net/se" 
  version="1.1.0">
  <layer>
    <se:Name>My Layer</se:Name>
  </layer>
</sld>

But if I add a default (unnamed) namespace then setting the namespace for the inner element does nothing. Change one line above to:

z = xmlTree("sld",
            namespaces=list("http://www.o.net/sld",
                "ogc"="http://www.o.net/ogc",
                "se"="http://www.o.net/se"),
            attrs=list(version="1.1.0")
            )

and that gives me:

<?xml version="1.0"?>
  <sld 
    xmlns="http://www.o.net/sld" 
    xmlns:ogc="http://www.o.net/ogc"
    xmlns:se="http://www.o.net/se"
   version="1.1.0">
  <layer>
    <Name>My Layer</Name>
  </layer>
</sld>

which has all the namespaces correct in the root element but the namespace I wanted for the inner element has gone.

Note I started working on this with newXMLDoc and newXMLNode functions, until I read the docs which said:

 While the
 functions are available, their direct use is not encouraged.
 Instead, use ‘xmlTree’ as the functions need to be used within a
 strict regime to avoid corrupting C level structures.

I'm at the point of giving up constructing these things with package::XML and just building them with a template system (package::brew, probably).

like image 791
Spacedman Avatar asked Oct 19 '25 23:10

Spacedman


2 Answers

Here's a slight variant that also retains the default namespace. FWIW I think XML::newXMLNode() (in my code) and z$addNode (in the other answer) are acting counter to the docs and examples in DTL's book (?). You are supposed to be able to specify the "se" namespace here and have it resolve through the namespace definitions in the ancestors. But that doesn't seem to be the case.

library(XML)

makesimple <- function() {
  z <- newXMLNode("sld", newXMLNode("layer"), attrs = list(version="1.1.0"),
                  namespaceDefinitions = c("http://www.o.net/sld",
                                           ogc = "http://www.o.net/ogc",
                                           se = "http://www.o.net/se"))
  z_doc <- newXMLDoc(node = z)
  Name <- newXMLNode("Name", "My Layer", parent = z[["layer"]])
  setXMLNamespace(Name, "se")
  z_doc
}
cat(saveXML(makesimple()))
#> <?xml version="1.0"?>
#> <sld xmlns="http://www.o.net/sld" xmlns:ogc="http://www.o.net/ogc" xmlns:se="http://www.o.net/se" version="1.1.0">
#>   <layer>
#>     <se:Name>My Layer</se:Name>
#>   </layer>
#> </sld>

It seems another way to force the namespace resolution is to specify a namespace prefix at the time of node creation, but to NOT specify the parent. Then when the node is added as a child, the namespace does get resolved correctly from the ancestors.

library(XML)

makesimple2 <- function() {
  z <- newXMLNode("sld", newXMLNode("layer"), attrs=list(version="1.1.0"),
                  namespaceDefinitions = c("http://www.o.net/sld",
                                           ogc = "http://www.o.net/ogc",
                                           se = "http://www.o.net/se"))
  z_doc <- newXMLDoc(node = z)
  Name <- newXMLNode("se:Name", "My Layer")
  addChildren(z[["layer"]], Name)
  z_doc
}
cat(saveXML(makesimple2()))
#> <?xml version="1.0"?>
#> <sld xmlns="http://www.o.net/sld" xmlns:ogc="http://www.o.net/ogc" xmlns:se="http://www.o.net/se" version="1.1.0">
#>   <layer>
#>     <se:Name>My Layer</se:Name>
#>   </layer>
#> </sld>
like image 78
jennybryan Avatar answered Oct 21 '25 12:10

jennybryan


Looks like the namespace argument to addNode is ignored for this purpose, but here's a work-around:

require(XML)
makesimple = function(){
    z = xmlTree("sld",
                namespaces=list("http://www.o.net/sld",
                    "ogc"="http://www.o.net/ogc",
                    "se"="http://www.o.net/se"),
                attrs=list(version="1.1.0")
                )
    z$addNode("layer", close=FALSE)
    # addNode returns the node invisibly, assign so we can use it
    nameNode <- z$addNode("Name","My Layer")
    setXMLNamespace(nameNode, "se")
    z$closeTag()
    z
}
cat(saveXML(makesimple()))
# <?xml version="1.0"?>
# 
# <sld xmlns:ogc="http://www.o.net/ogc" xmlns:se="http://www.o.net/se" version="1.1.0">
#   <layer>
#     <se:Name>My Layer</se:Name>
#   </layer>
# </sld>

Basically, we use the fact that addNode returns the node invisibly. We assign that value to a temporary object, so we can use the setXMLNamespace function on that object.

like image 40
Joshua Ulrich Avatar answered Oct 21 '25 13:10

Joshua Ulrich



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!