Problem
You want to work with and access information in an XML file.
Solution
Use PowerShell’s XML cast to convert the plaintext XML into a form that you can more easily work with. In this case, the RSS feed downloaded from the Windows PowerShell blog:
PS >$xml = [xml] (GetContent powershell_blog.xml)
Like other rich objects, PowerShell displays the properties of the XML as you explore. These properties are child nodes and attributes in the XML, as shown by Example 81.
Example 81. Accessing properties of an XML document
PS >$xml
xml
xmlstylesheet
rss
rss
PS >$xml.rss
version : 2.0 dc : http://purl.org/dc/elements/1.1/ slash : http://purl.org/rss/1.0/modules/slash/ wfw : http://wellformedweb.org/CommentAPI/ channel : channel
If more than one node shares the same name (as in the item nodes of an RSS feed), then the property name represents a collection of nodes:
PS >($xml.rss.channel.item).Count 15
You can access those items individually, like you would normally work with an array, as shown in Example 82.
Example 82. Accessing individual items in an XML document
PS >($xml.rss.channel.item)[0]
description : <P>Since a lot of people have been asking about it, yes my
(...) guid : guid title : "Windows PowerShell in Action" has been released comment : http://blogs.msdn.com/powershell/rsscomments.aspx?PostID=171
8281 link : http://blogs.msdn.com/powershell/archive/2007/02/19/windows
powershellinactionhasbeenreleased.aspx pubDate : Mon, 19 Feb 2007 20:05:00 GMT comments : {4, http://blogs.msdn.com/powershell/comments/1718281.aspx} commentRss : http://blogs.msdn.com/powershell/commentrss.aspx?PostID=1718
281 creator : PowerShellTeam
You can access properties of those elements like you would normally work with an object:
PS >($xml.rss.channel.item)[0].title
"Windows PowerShell in Action" has been released
Since these are rich PowerShell objects, Example 83 demonstrates how you can use PowerShell’s advanced objectbased cmdlets for further work, such as sorting and filtering.
Example 83. Sorting and filtering items in an XML document
PS >$xml.rss.channel.item | SortObject title | SelectObject title
title
"Windows PowerShell in Action" has been released Controlling PowerShell Function (Re)Definition Execution Policy and Vista Executive Demo It's All about Economics NetCmdlets Beta 2 is now Available. Payette Podcast Port 25 interview with Bruce Payette PowerShell Benefits Over COM Scripting PowerShell Cheat Sheet Now in XPS PowerShell Tip: How to "shift" arrays Processing text, files and XML Virtual Machine Manager's PowerShell Support Windows PowerShell 1.0 for Windows Vista Working With WMI Events
Discussion
PowerShell’s native XML support provides an excellent way to easily navigate and access XML files. By exposing the XML hierarchy as properties, you can perform most tasks without having to resort to textonly processing, or custom tools.
In fact, PowerShell’s support for interaction with XML goes beyond just presenting your data in an objectfriendly way. The objects created by the [xml] cast in fact represent fully featured System.Xml.XmlDocument objects from the .NET Framework. Each property of the resulting objects represents a System.Xml.XmlElement object from the .NET Framework, as well. The underlying objects provide a great deal of additional functionality that you can use to perform both common and complex tasks on XML files.
The underlying System.Xml.XmlDocument and System.Xml.XmlElement objects that support your XML provide useful properties in their own right, as well: Attributes, Name, OuterXml, and more. Since these properties may interfere with the way you access properties from your XML file, PowerShell hides them by default. To access them, use the PsBase property on any node. The PsBase property works on any object in PowerShell, and represents the object underneath the PowerShell abstraction:
PS >$xml.rss.psbase.Attributes
#text
2.0 http://purl.org/dc/elements/1.1/ http://purl.org/rss/1.0/modules/slash/ http://wellformedweb.org/CommentAPI/
Perform an XPath Query Against an XML File
Problem
You want to perform an advanced query against an XML file, using XML’s standard XPath syntax.
Solution
Use PowerShell’s XML cast to convert the plaintext XML into a form that you can more easily work with. In this case, the RSS feed downloaded from the Windows PowerShell blog:
PS >$xml = [xml] (GetContent powershell_blog.xml)
Then use the SelectNodes() method on that variable to perform the query. For example, to find all post titles shorter than 20 characters:
PS >$query = "/rss/channel/item[stringlength(title) < 20]/title" PS >$xml.SelectNodes($query)
#text
Payette Podcast Executive Demo
Discussion
Although a language all its own, the XPath query syntax provides a powerful, XMLcentric way to write advanced queries for XML files.
Modify Data in an XML File
Problem
You want to use PowerShell to modify the data in an XML file.
Solution
To modify data in an XML file, load the file into PowerShell’s XML data type, change the content you want, and then save the file back to disk. Example 84 dem onstrates this approach.
Example 84. Modifying an XML file from PowerShell
PS >## Store the filename PS >$filename = (GetItem phone.xml).FullName PS > PS >## Get the content of the file, and load it PS >## as XML PS >GetContent $filename <AddressBook>
<Person contactType="Personal"> <Name>Lee</Name> <Phone type="home">5551212</Phone> <Phone type="work">5551213</Phone>
</Person>
<Person contactType="Business"> <Name>Ariel</Name> <Phone>5551234</Phone>
</Person> </AddressBook> PS >$phoneBook = [xml] (GetContent $filename) PS >
Example 84. Modifying an XML file from PowerShell (continued)
PS >## Get the part with data we want to change PS >$person = $phoneBook.AddressBook.Person[0] PS > PS >## Change the text part of the information, PS >## and the type (which was an attribute) PS >$person.Phone[0]."#text" = "5551214" PS >$person.Phone[0].type = "mobile" PS > PS >## Add a new phone entry PS >$newNumber = [xml] '<Phone type="home">5551215</Phone>' PS >$newNode = $phoneBook.ImportNode($newNumber.Phone, $true) PS >[void] $person.AppendChild($newNode) PS > PS >## Save the file to disk PS >$phoneBook.Save($filename) PS >GetContent $filename <AddressBook>
<Person contactType="Personal"> <Name>Lee</Name> <Phone type="mobile">5551214</Phone> <Phone type="work">5551213</Phone> <Phone type="home">5551215</Phone>
</Person>
<Person contactType="Business"> <Name>Ariel</Name> <Phone>5551234</Phone>
</Person> </AddressBook>
Discussion
In the preceding solution, you change Lee’s phone number (which was the “text” portion of the XML’s original first Phone node) from 5551212 to 5551214. You also change the type of the phone number (which was an attribute of the Phone node) from "home" to "mobile".
Adding new information to the XML is nearly as easy. To add information to an XML file, you need to add it as a child node to another of the nodes in the file. The easiest way to get that child node is to write the string that represents the XML and then create a temporary PowerShell XML document from that. From that document, you use the main XML document’s ImportNode() function to import the node you care about—specifically, the Phone node in this example.
Once we have the child node, you need to decide where to put it. Since we want this Phone node to be a child of the Person node for Lee, we will place it there. To add a child node ($newNode , in Example 84) to a destination node ( $person, in the example), use the AppendChild() method from the destination node.
The Save() method on the XML document allows you to save to more than just files. For a quick way to convert XML into a “beautified” form, save it to the console:
$phoneBook.Save([Console]::Out)
Finally, we save the XML back to the file from which it came.