Monday, July 26, 2010

WSMAN Namespace Handling in PowerShell

For some time now I have been working on handling XML with PowerShell – not XML that I make mind you, that appears to be relatively easy as the plethora of examples out there keeps showing me.

I am handling XML that I get back as a blob from a call to a WS-MAN provider.  It has Namespaces – that changes the game big time.

The best general reference I have found is Dr. Tobias Weltner (he is the brilliant person behind PowerShellPlus – which is an IDE that I simply don’t know how people write complex PowerShell scripts without).  This article; talks about XML and PowerShell, but it misses the one thing that I needed, Namespace handling. 

A bit of digging let me to a C# article about xpath and xml namespaces – that sent me to the real tidbit I needed Select-Xml;

First I needed to workout what my namespace selection problem really was.  Here is the mess that I get back:

<n1:SomeCimMethod_OUTPUT xmlns:n1= xmlns:wsa= xmlns:wsman= xmlns="" xml:lang=""> <n1:ThingOne><wsa:Address></wsa:Address> <wsa:ReferenceParameters><wsman:ResourceURI></wsman:ResourceURI><wsman:SelectorSet><wsman:Selector name="__cimnamespace">root/cimv2</wsman:Selector><wsman:Selector Name="CreationClassName">Image</wsman:Selector><wsman:Selector Name="ID">2c8ba04e-53b8-504d-f616-061a43bb46bf/969f4a72-4a0d-4044-b41e-f3025377d067</wsman:Selector><wsman:Selector Name="CreationClassName">Creator</wsman:Selector><wsman:Selector Name="Name">2c8ba04e-53b8-504d-f616-061a43bb46bf</wsman:Selector></wsman:SelectorSet></wsa:ReferenceParameters></n1:ThingOne><n1:ThingTwo>57702fd0-9e92-43dc-9ac6-537719b73473</n1:ThingTwo><n1:ThingThree>4e4449df-8710-4358-8290-44d7b4264d46=403ef95b-0309-417e-86d8-c75066439419,c735019c-2198-4d53-a6ac-668d38e6a81d=eb15c741-5a05-4377-91b5-7bd95ab21f3d,2b2ad08b-ecdf-42de-9f03-1050862b99fb=e2aae65c-dd64-49f3-a796-e12fecdc2b46,97c47f43-55af-438f-83b9-2d4a01733ce7=fff39bf0-9d21-4475-b7c7-9e96eb35e8d8,ee8e54e2-b499-438f-a62f-67c024e5921a=ebc63996-6399-4ffd-a5ad-6bd0dcf2036f,6fa65d8c-7cbb-438f-a2ea-35e498c525c5=ae960929-4cd6-42b3-9159-f4e0119cae92,80855f0f-1e22-44bf-892c-c8ca1fd7af59=30dd2807-5566-4655-822a-4f6780f0fdaa,57702fd0-9e92-43dc-9ac6-537719b73473=969f4a72-4a0d-4044-b41e-f3025377d067</n1:ThingThree><n1:ThingFour>57702fd0-9e92-43dc-9ac6-537719b73473</n1:ThingFour><n1:ReturnValue>0</n1:ReturnValue></n1:SomeCimMethod_OUTPUT>

If you look into this blob (there is a good reason developers call these blobs) you will see that each element is preceeded by the namespace “n1”.  Howerver, if you simply cast this to $blob = [xml]$blob it looks entirely different and you don’t really realize that each element is part of namespace “n1”.

PS > $blob.SomeCimMethod_OUTPUT

n1            :
wsa           :
wsman         :
xmlns         :
lang          :
ThingOne  : ThingOne
ThingTwo     : 57702fd0-9e92-43dc-9ac6-537719b73473
ThingThree  : 4e4449df-8710-4358-8290-44d7b4264d46=403ef95b-0309-417e-86d8-c75066439419,c735019c-2198-4d53-a6ac-668d38e6a81d=eb15c741-5a05-4377-91b5-7bd95ab21f3d,2b2ad08b-ecdf-42de-9f03-1050862b99fb=e2aae65c-dd64-49f3-a796-e12fecdc2b46,97c47f43-55af-438f-83b9-2d4a01733ce7=fff39bf0-9d21-4475-b7c7-9e96eb35e8d8,ee8e54e2-b499-438f-a62f-67c024e5921a=ebc63996-6399-4ffd-a5ad-6bd0dcf2036f,6fa65d8c-7cbb-438f-a2ea-35e498c525c5=ae960929-4cd6-42b3-9159-f4e0119cae92,80855f0f-1e22-44bf-892c-c8ca1fd7af59=30dd2807-5566-4655-822a-4f6780f0fdaa,57702fd0-9e92-43dc-9ac6-537719b73473=969f4a72-4a0d-4044-b41e-f3025377d067
ThingFour : 57702fd0-9e92-43dc-9ac6-537719b73473
ReturnValue   : 0

In my example I am looking for the element “ThingTwo” which is really “n1:ThingTwo”.  The detail is that it exists within namespace “n1” and because of that $blob.SelectNodes and $blob.SelectSingleNode were totally failing me.

So, how do I find a single element within this?

First, my $blob has to be an XML document, in this case the return from the WS-MAN provider is all formatted properly, I just need to cast it to an XML document (as in PowerShell everything is a generic type of Object by default).

$blob = [xml]$blob

$blob.GetType() should return “XmlDocument” as the Name.

Then i have to make the XML parser aware of the namespace and pass that into the Select-Xml method.

$namespace = @{n1=}

Now I can use Select-Xml to find my element.

Select-Xml -Xml $blob -Xpath "//n1:ThingTwo" -Namespace $namespace

1 comment:

Anonymous said...

Thanks for the post. It was very informative.