Summary: Microsoft Scripting Guy, Ed Wilson, talks about calling XMLDocument methods in Windows PowerShell.
Microsoft Scripting Guy, Ed Wilson, is here. Today is Friday the 13th. Cool. Luckily, I do not have to go anywhere tonight around midnight, by the light of a full moon. But I am going to use Windows PowerShell methods to slice and dice some XML. Luckily, this is a very neat technique, and it will not leave any wayward bits of data laying around. Besides, I am listening to Scripting Cmdlet Style, and that should scare off anything that is non-PowerShell related.
Note This is the fifth in a series of posts about working with XML. You might also enjoy reading:
- Creating an XML Document for Admins
- Exploring XML Document by Using the [XML] Type Accelerator
- Using PowerShell to Explore Structure of XML Document
- Calling XML Document Properties from Within PowerShell
Although Windows PowerShell makes it easy to navigate XML documents, at times (because of the size of the files, the numbers of nodes, or some other non-PowerShell related element), one may want to drop down to the .NET Framework to do some work.
To make things a bit more interesting today, I decided to use an XML document from MSDN called Sample XML File (books.xml). All one needs to do to use it is to copy the code from the MSDN page, paste it into a plain Notepad text file, and rename the file Books.xml. After I have done that, I open it in XML Notepad for fun. This is what I find:
One of my favorite XMLDocument methods is the SelectNodes method. It will accept an XPATH path, which is simply a specially formatted string. The easiest XPATH query to use is “*”, which means, "Dude, show me everything."
In the following example, I first read in the XML by using Get-Content and the [XML] type accelerator. I then use the ChildNodes property and the SelectNodes method.
[xml]$xmldocument = Get-Content C:\fso\books.xml
$xmldocument.ChildNodes.selectNodes("*")
The commands and the associated output from the commands is shown here:
Because I see that my query with the wildcard character (“*”) worked as I thought it would, I decide to get brave and return only the titles. I do this by using the forward slash ( / ) symbol as the separator, and I simply call the Title node that I saw in the output from my first query. The query is shown here:
$xmldocument.ChildNodes.selectNodes("*/title")
The following image shows the query and the associated output:
I can also pick up the Genre of the books, as shown here:
$xmldocument.ChildNodes.selectNodes("*/genre")
The command and associated output are shown here:
The list of genres looks cool, but I really only want to know what genres are represented—that is, I want a unique list. Because this is Windows PowerShell, I can easily find this information in a couple of ways. The way I like to do this is to use Select-Object and specify the –Unique switch:
$xmldocument.ChildNodes.selectNodes("*/genre") | select '#text' –Unique
The output is nice, and it lets me know only the genres that are unique in the list:
Maybe I want to know the value of all of my books, and how many books I actually have. This is easy to do with Windows PowerShell, XML, and XPath. Here is the command I use:
$xmldocument.ChildNodes.SelectNodes("*/price") | measure '#text' –Sum
Here is the command and the output from the command:
Now I want to add one of my books to the Books.xml file. To do this, I will first clone one of the book nodes, as shown here:
$copy = $xmldocument.catalog.book[0].Clone()
Now, I simply assign new values for each of the properties. This appears here:
$copy.id = 'bk113'
$copy.author = 'Wilson, Ed'
$copy.title = 'Windows PowerShell Best Practices'
$copy.price = '59.99'
$copy.publish_date = '2014-01-25'
$copy.description = 'Automate system administration using Windows PowerShell best practices’
Now, I want to add the new node to the XML. I do this by calling the appendchild method and passing the $copy. This appears here:
$xmldocument.catalog.appendchild($copy)
I can now check the child nodes to ensure that it is added:
$xmldocument.ChildNodes.childnodes
Now I write the InnerXML to a text file with the XML extension:
$xmldocument.InnerXml >>c:\fso\modified.xml
I can now open it in XML Notepad to see if it worked properly. Here is my output:
That is all there is to using XML methods. Join me tomorrow for the Weekend Scripter when I will talk about non-PowerShell books for Windows PowerShell people.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy