PowerShell_2 Welcome to this addition of the PowerShell ABC's where you'll find 26 posts detailing a component of the PowerShell scripting language, one letter at a time.  For today's letter of "X", I'll discuss PowerShell's ability to work natively with XML.

xmlLogo XML (Extensible Markup Language) is being used more and more in today's computing environments.  PowerShell is no exception in that it uses XML for its type and configuration files as well as for it's help system. 

Since PowerShell uses XML so much internally, it makes sense that it has to expose a way to process XML documents in a simple way.

The [xml] Data Type

PowerShell supports XML documents as a primitive data type.  This is powerful in that it allows you to access a XML Document just as you would an object with elements and child elements being properties on that object.  By using the [xml] type literal you can convert a string containing XML text into a System.XML.XmlDocument object.  The following example illustrates creating a XML object and 

PS C:\> $x = [xml]"hithere"
PS C:\> $x | Get-Member
   TypeName: System.Xml.XmlDocument

Name                        MemberType            Definition
----                        ----------            ----------
ToString                    CodeMethod            static System.String XmlNode(PSObject instance)
add_NodeChanged             Method                System.Void add_NodeChanged(XmlNodeChangedEventHandler value)
add_NodeChanging            Method                System.Void add_NodeChanging(XmlNodeChangedEventHandler value)
add_NodeInserted            Method                System.Void add_NodeInserted(XmlNodeChangedEventHandler value)
add_NodeInserting           Method                System.Void add_NodeInserting(XmlNodeChangedEventHandler value)
add_NodeRemoved             Method                System.Void add_NodeRemoved(XmlNodeChangedEventHandler value)
add_NodeRemoving            Method                System.Void add_NodeRemoving(XmlNodeChangedEventHandler value)
AppendChild                 Method                System.Xml.XmlNode AppendChild(XmlNode newChild)
Clone                       Method                System.Xml.XmlNode Clone()
CloneNode                   Method                System.Xml.XmlNode CloneNode(Boolean deep)
PS C:\> $x

PS C:\> $x.a.b

Manipulating XML Documents

Since the [xml] type object is a true object, you can do more than look at it.  You have the full abilities to add, modify, and remove content with the underlying System.Xml.XmlDocument methods and properties.  The following example adds and removes child elements with the AppendChild and RemoveChild XmlDocument methods.

PS C:\> $x = New-Object -TypeName xml
PS C:\> $el = $x.CreateElement("root")
PS C:\> $x.AppendChild($el)
PS C:\> $x
PS C:\> $x.RemoveChild($el)
PS C:\> $x.get_HasChildNodes()
PS C:\> $x.AppendChild($el)
PS C:\> $x.get_HasChildNodes()
PS C:\> $x

Loading and Saving XML Files

You can load and save XML documents to and from the file system with the Load() and Save() methods in the XmlDocument object.  The following illustrates how to save the previously created document and then reload it again into another variable.

PS C:\> $x.Save("c:\temp\foo.xml")
PS C:\> Get-Content c:\temp\foo.xml

PS C:\> $x2 = New-Object xml
PS C:\> $x2.Load("c:\temp\foo.xml")

Serializing objects

There are a couple of Cmdlet's that are specifically designed for the serialization of objects.  Serialization is the process of saving and restoring an object or objects to a file or network stream.  The Two Cmdlet's are Export-Clixml and Import-Clixml.  In the following example, I'll show how to export the contents of an arbitrary object to an XML stream and restore it.

PS C:\> $o = New-Object -TypeName System.Object
PS C:\> $o | Add-Member -MemberType noteProperty -Name name1 -Value value1
PS C:\> $o | Add-Member -MemberType noteProperty -Name name2 -Value value2
PS C:\> $o | Export-Clixml -Path c:\temp\foo.xml
PS C:\> Get-Content -Path c:\temp\foo.xml



PS C:\> $o2 = Import-Clixml -Path c:\temp\foo.xml
PS C:\> $o2
name1                                                       name2
-----                                                       -----
value1                                                      value2

These Cmdlets provide an easy way to save and restore collections of objects but there are some limitations.  These Cmdlets can only load and save a fixed number of primitive types.  Other types are broken apart, or shredded, into a property bag composed of primitive types.  Unfortunately, this means that objects with non-primitive types, cannot be restored to exactly the same type they were originally.

Another issue with serialization is the matter of property depth.  An object can have properties, which in turn can have properties, and so on.  The chain of properties that have properties is referred to as property depth.  Since this could ultimately yield a very large output file for even the simplest of objects, the Export-Cmlxml defaults to a property depth of 2 for exports.  You can override this with the -depth parameter if you wish.