Hi all,
Doing quite a bit of Orchestration with vCloud etc at the moment.
I have just started using E4X for XML queries etc. I find it pretty easy to use if a little poorly documented..
In vCloud object's XML representation include a large number of different namespaces and I have found it pretty annoying trying to declare them all.
consider the following:
sXML = "<parent><vmext:EntityLink rel="up" type="vcloud:org" name="System" id="urn:vcloud:org:a93c9db9-7471-3192-8d09-a8f7eeda85f9"/></parent>"
Now, say I want to return the EntityLink element from this:
oXML = XML(sXML);
System.log(oXML.vmext::EntityLink)
would return something like "vmext undefined"
This is because we have not declared aliases for the namespaces.
Namespace alias' can be declared thusly:
var vmext = new Namespace("http://urlofnamespace...");
The problem comes when we are looking at XMLDocs with large numbers of namespaces (vCloud often uses the format ns1: ns2: etc..)
A well formed XML document will declare these Namespaces internally via the xmlns attributes. These are automatically added to the XML object when it is constructed and can be retrieved via:
XML.namespaceDeclarations() -> returns an aray of http urls
Unfortunately I could not find a way of returning the aliases for these namespaces directly from the XML object so I wrote the following function which works but is rather ugly in my opinion:
function DeclareNS(xmlstring){
var nsNames = xmlstring.match(/xmlns:?[^\s]+/g);
var xOut = new XML(xmlstring);
for (var i=0,iMax=nsNames.length;i<iMax;i++){
var nUrl = nsNames[i].split("=")[1];
var aName = nsNames[i].split("=")[0].split(":");
if (aName.length == 2){
var nName = aName[1];
eval("this."+nName +"= new Namespace("+nUrl+")");
System.log("Creating Namespace Definition: "+nName+"="+nUrl);
}else{
default xml namespace = nUrl;
System.log("Setting Default Namespace: "+nUrl);
}
}
return xOut;
}
This will Declare ALL namespaces referenced by an xmlns attribute in the document and return a constructed XML Object. All we now need to do is call DeclareNS each time we need a new XML object (from string) and we can write nice E4X queries to access the data we need.
Anyway, I hope that is useful to someone. I would love to hear from you if you have suggestions on how to declare Namespaces easier!!
If you have the namespaces declaration in the XML wouldn't this work ?
default xml namespace = oXML.namespace();
I was surprised to see that oXML = XML(sXML) works. I have always used oXML = new XML(sXML);
as far as I know that only allows access to the default namespace. I may very well be wrong though so I will test and check.
You may be right since I remember having to use .*:: to get access to the elements.
Yep, just tested and default xml namespace = oXML.namespace(); unfortunately doesnt work.
Also noticed there are some issues with the above function.
It seems that namespace declarations can be xmlns= or xmlns:ns= so need to re-do the regex
Will post back in a bit.
Ok,
I have updated it now. If it finds a namepace attribute without a : it declares it as the default namespace (I presume that is what it should be)
I dont think there will ever be more than one of these but if there are then only the last one found will be set.
Just tested it on a vCloud Task XML Doc and it set 7 NS* aliases plus 1 default.
Seems to work quite well 🙂
Hope it works for someone else too.
Tim
I did something similar using namespaceDeclarations();
default xml namespace = xml.namespace();
var namespaceDeclarations = xml.namespaceDeclarations();
for each (var namespaceDeclaration in namespaceDeclarations) {
//xml.removeNamespace(namespaceDeclaration);
if (namespaceDeclaration.prefix.length >0) {
System.log(namespaceDeclaration.prefix + ' = new Namespace("' + namespaceDeclaration.uri + '");');
eval(namespaceDeclaration.prefix + ' = new Namespace("' + namespaceDeclaration.uri + '");');
}
}
Christophe.