<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>XML</title><link>http://www.pluralsight.com/blogs/dan/category/82.aspx</link><description>Things involving pointy brackets, interoperable data, and things that use XML.</description><managingEditor>Dan Sullivan</managingEditor><dc:language>en-US</dc:language><generator>.Text Version 0.95.2004.102</generator><item><dc:creator>Dan Sullivan</dc:creator><title>Processing XML with PowerShell II</title><link>http://pluralsight.com/blogs/dan/archive/2006/11/28/43561.aspx</link><pubDate>Tue, 28 Nov 2006 13:12:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/11/28/43561.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/43561.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/11/28/43561.aspx#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/43561.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/43561.aspx</trackback:ping><description>&lt;P&gt;In our previous blog article &lt;A href="http://www.pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx"&gt;&lt;I&gt;Processing XML with PowerShell&lt;/I&gt;&lt;/A&gt; we looked at using XPath expressions to do calculations that used XML as input. One of the things that this article pointed out was that you often can do an entire calculation within an XPath expression. Sometimes, however, you want to read the XML file, pull parts out of it and process them outside of the XML file. One of the ways of doing this is to use the dotted syntax and the Item ParameterizedProperty features of PowerShell so that you can treat XML as an object.&lt;/P&gt;
&lt;P&gt;We are going to start off by looking at using the PowerShell object model for XML to pull apart an XML file so that we can process it, then we are going to look at an extension function aliased as xitems to do the same thing, but typically with less effort and more capabilities. Lastly we will look at how the xitems function is implemented. You can download the script for making this function and the sample files at &lt;A href="http://www.pluralsight.com/dan/samples/ProcessingXMLPowershell-2.zip"&gt;http://www.pluralsight.com/dan/samples/ProcessingXMLPowershell-2.zip&lt;/A&gt;. Note that this will include the XSLT.ps1 script, updated for the xitems function, that was include in the ProcessingXMLPowerShell.zip file.&lt;/P&gt;
&lt;P&gt;We will start by looking at a file named Stock.xml. The Stock.xml file looks like:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;GroceryList
ID = "A-24"
&amp;gt;
&amp;lt;Stock&amp;gt;
    &amp;lt;Dept&amp;gt;
    &amp;lt;Area&amp;gt;Round&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Produce&amp;lt;/Name&amp;gt;
    &amp;lt;/Dept&amp;gt;
    &amp;lt;Name&amp;gt;Orange&amp;lt;/Name&amp;gt;
&amp;lt;Price&amp;gt;3.41&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock&amp;gt;
    &amp;lt;Dept&amp;gt;
    &amp;lt;Area&amp;gt;Beef&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Meat&amp;lt;/Name&amp;gt;
    &amp;lt;/Dept&amp;gt;
    &amp;lt;Name&amp;gt;Steak&amp;lt;/Name&amp;gt;
    &amp;lt;Price &amp;gt;13.20&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock&amp;gt;
    &amp;lt;Dept&amp;gt;
    &amp;lt;Area&amp;gt;Leaf&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Produce&amp;lt;/Name&amp;gt;
    &amp;lt;/Dept&amp;gt;
    &amp;lt;Name&amp;gt;Lettuce&amp;lt;/Name&amp;gt;
    &amp;lt;Price&amp;gt;1.36&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;/GroceryList&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that each Dept element includes both an Area and Name element. We want to find out the names of the departments in the stock.xml file. We can do this by piping all of the department names into a select-object -unique cmdlet. Here is a script that does just that.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; [xml]$s = get-content C:\Demos\Stock.xml
PS C:\Demos&amp;gt; $s.GroceryList.Stock | %{$_.Dept.Name} | select-object -unique
Produce
Meat
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we are using the dotted syntax to extract each Stock element from the stock.xml file and pipe it into a following pipeline segment. In that second pipeline segment we use the dotted syntax again to extract the Name from the Dept element of current pipeline object; The current pipeline object in this case is the Stock element. Then we pipe all the names we have found into the select-object cmdlet that makes a unique list of those names. &lt;/P&gt;
&lt;P&gt;It&amp;#8217;s easy to come up with a rather wordy description of what this script is doing; It is saying something like &lt;SPAN id=hierarchy&gt;&amp;#8220;Give me all of the elements named GroceryList at the root of the $s XML document; then for each one of these get me all of its children whose name is Stock; then for each one of these get me all of its children whose name is Dept; then for each on of these get me all of its children whose name is Name.&amp;#8221;&lt;/SPAN&gt; This sort of description could be applied to almost anything that manages hierarchical data including XPath, which we will be looking at later when we examine the xitems function.&lt;/P&gt;
&lt;P&gt;Thinking of this hierarchical description makes it seem that the script below would be alternate, more simple, way to find the names of the departments.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; [xml]$s = get-content C:\Demos\Stock.xml
PS C:\Demos&amp;gt; $s.GroceryList.Stock.Dept.Name | select-object -unique
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This, seemingly obvious, way of getting the children of the children, &lt;I&gt;etc.&lt;/I&gt; did not produce any results. The dotted syntax is somewhat limited because of the way PowerShell models XML. If we take closer look at what is actually being returned for the GroceryList and Stock elements we will see why.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; $s.GroceryList.GetType()
IsPublic IsSerial Name       BaseType
-------- -------- ----      --------
True     False    XmlElement System.Xml.XmlLinkedNode
PS C:\Demos&amp;gt; $s.GroceryList.Stock.GetType()
IsPublic IsSerial Name      BaseType
-------- -------- ----      --------
True     True     Object[]  System.Array
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The type of the GroceryList is an XmlElement as you might suspect, after all we are working with XML. However the type of Stock is Array, not an XmlElement or XmlElement[], and that is why it lacks a Dept property. Now let&amp;#8217;s look at what happens when instead of trying to access the Dept element from Stock we pipe the array into another pipeline segment and see what the type is.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; $s3.GroceryList.Stock | %{$_.GetType()}
IsPublic IsSerial Name        BaseType
-------- -------- ----        --------
True     False    XmlElement  System.Xml.XmlLinkedNode
True     False    XmlElement  System.Xml.XmlLinkedNode
True     False    XmlElement  System.Xml.XmlLinkedNode
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It turns out that the Stock array is an array of XmlElements and piping it into a pipeline segment enumerates that array. The result is that inside the pipeline segment $_ is an XmlElement that has a Dept child element.&lt;/P&gt;
&lt;P&gt;So to drill into an XML hierarchy using the PowerShell object model you must break up what seems like a natural dotted syntax into pipeline segments, one pipeline segment for every two levels of depth you want to go into the XML hierarchy. The &lt;A href="http://pluralsight.com/blogs/dan/admin/EditPosts.aspx#hierarchy"&gt;word description&lt;/A&gt; earlier of what is happening here, however, is in effect the definition of an XPath construct called a LocationPath.&lt;/P&gt;
&lt;P&gt;We used XPath expressions in &lt;A href="http://www.pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx"&gt;&lt;I&gt;Processing XML with PowerShell&lt;/I&gt;&lt;/A&gt; to do processing of an XML file. XPath is really a simple language, an expression can only produce one of four datatypes; A number, a string, a boolean, or a node set. We were using those first three scalar types to do calculations with the xeval function.&lt;/P&gt;
&lt;P&gt;A node set is what the name seems to imply, it is a set of XML nodes. It might be a set of XML elements or attributes or a mixture of these an other kinds of XML nodes. The data model in the &lt;A href="http://www.w3.org/TR/xpath"&gt;XPath Recommendation&lt;/A&gt; defines seven kinds of nodes that might be found in an XML documents. A &lt;A href="http://www.w3.org/TR/xpath#location-paths"&gt;LocationPath&lt;/A&gt; is an XPath expression that produces a node set instead of a scalar value. It is given a special name because it is such a common idiom to use XPath to produce a node set. Here is a LocationPath that will produce a nodeset that consists of all of the Name elements from the stock.xml file:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;GroceryList/Stock/Dept/Name
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We can use this LocationPath with the xitems function to find the names of the departments in the stock.xml file, as we did at the beginning of this article.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xitems C:\Demos\Stock.xml "GroceryList/Stock/Dept/Name" |
 select-object -property value -unique
Value
-----
Produce
Meat
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The xitems function requires at least two parameters. The first is the path to the XML file we want to process, or as we will later see an XPathNavigator. The second argument is an XPath LocationPath. You can see we are able to use a more simple model to specify the parts of the XML file we wish to process. &lt;/P&gt;
&lt;P&gt;In the &lt;A href="http://www.pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx"&gt;&lt;I&gt;Processing XML with PowerShell&lt;/I&gt;&lt;/A&gt; blog article we looked at processing XML files that contained namespaces and some of the issues you can run into when using the PowerShell object model of XML. The xitems function uses the same technique as the xeval did, you pass a dictionary that contains the mapping of prefixes and namespaces to it. Here is another file that uses namespaces, stockNS.xml.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;GroceryList xmlns='urn:prices'
xmlns:loc='urn:location'
xmlns:ident='urn:identity'
ID = "A-24"
&amp;gt;
&amp;lt;Stock xmlns="urn:inventory"&amp;gt;
    &amp;lt;loc:Dept&amp;gt;3rd floor&amp;lt;/loc:Dept&amp;gt;
    &amp;lt;ident:Dept&amp;gt;
    &amp;lt;Area&amp;gt;Round&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Produce&amp;lt;/Name&amp;gt;
    &amp;lt;/ident:Dept&amp;gt;
    &amp;lt;Name&amp;gt;Orange&amp;lt;/Name&amp;gt;
&amp;lt;Price&amp;gt;3.41&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock xmlns="urn:inventory"&amp;gt;
    &amp;lt;loc:Dept&amp;gt;2nd floor&amp;lt;/loc:Dept&amp;gt;
    &amp;lt;ident:Dept&amp;gt;
    &amp;lt;Area&amp;gt;Beef&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Meat&amp;lt;/Name&amp;gt;
    &amp;lt;/ident:Dept&amp;gt;
    &amp;lt;Name&amp;gt;Steak&amp;lt;/Name&amp;gt;
    &amp;lt;Price &amp;gt;13.20&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock xmlns="urn:inventory"&amp;gt;
    &amp;lt;loc:Dept&amp;gt;3rd floor&amp;lt;/loc:Dept&amp;gt;
    &amp;lt;ident:Dept&amp;gt;
    &amp;lt;Area&amp;gt;Leaf&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Produce&amp;lt;/Name&amp;gt;
    &amp;lt;/ident:Dept&amp;gt;
    &amp;lt;Name&amp;gt;Lettuce&amp;lt;/Name&amp;gt;
    &amp;lt;Price&amp;gt;1.36&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;/GroceryList&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This file uses quite a few namespaces, just to make things interesting and because typically when namespaces are used they are often used a lot. Lets do our department names calculation again, using the PowerShell xml object model.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; [xml]$s = get-content C:\Demos\StockNS.xml
PS C:\Demos&amp;gt; $s.grocerylist.stock 
  | %{$_.Item("Dept", "urn:identity").Name} | select-object -unique
Produce
Meat
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have used the Item property that PowerShell adds to an XML element to make it possible to access elements that are distinguished by their namespace. Here is a script that uses xitems to get the same results:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xitems C:\Demos\StockNS.xml `
  "p:GroceryList/i:Stock/id:Dept/i:Name" `
  @{p="urn:prices";i="urn:inventory";id="urn:identity"} |
  select-object -property value -unique
Value
-----
Produce
Meat

PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;You might look at this and say that the PowerShell object model for XML is in at least one respect much easier to use than one based on XPath that xitems uses because in the it does not require the specification of the namespaces for the GroceryList, Stock and Name elements. However namespaces are part of an XML file for a reason, to make sure that names in common usage can easily be distinguished. Look at this alternate version of the StockNS.xml file:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;GroceryList xmlns='urn:prices'
xmlns:loc='urn:location'
xmlns:ident='urn:identity'
ID = "A-24"
&amp;gt;
&amp;lt;Stock xmlns="urn:inventory"&amp;gt;
    &amp;lt;loc:Dept&amp;gt;3rd floor&amp;lt;/loc:Dept&amp;gt;
    &amp;lt;ident:Dept&amp;gt;
    &amp;lt;Area&amp;gt;Round&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Produce&amp;lt;/Name&amp;gt;
    &amp;lt;/ident:Dept&amp;gt;
    &amp;lt;Name&amp;gt;Orange&amp;lt;/Name&amp;gt;
&amp;lt;Price&amp;gt;3.41&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock xmlns="urn:ignore"&amp;gt;
    &amp;lt;loc:Dept&amp;gt;2nd floor&amp;lt;/loc:Dept&amp;gt;
    &amp;lt;ident:Dept&amp;gt;
    &amp;lt;Area&amp;gt;Beef&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Meat&amp;lt;/Name&amp;gt;
    &amp;lt;/ident:Dept&amp;gt;
    &amp;lt;Name&amp;gt;Steak&amp;lt;/Name&amp;gt;
    &amp;lt;Price &amp;gt;13.20&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock xmlns="urn:inventory"&amp;gt;
    &amp;lt;loc:Dept&amp;gt;3rd floor&amp;lt;/loc:Dept&amp;gt;
    &amp;lt;ident:Dept&amp;gt;
    &amp;lt;Area&amp;gt;Leaf&amp;lt;/Area&amp;gt;
    &amp;lt;Name&amp;gt;Produce&amp;lt;/Name&amp;gt;
    &amp;lt;/ident:Dept&amp;gt;
    &amp;lt;Name&amp;gt;Lettuce&amp;lt;/Name&amp;gt;
    &amp;lt;Price&amp;gt;1.36&amp;lt;/Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;/GroceryList&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Notice that the second Stock element is in the &amp;#8220;urn:ignore&amp;#8221; namespace, not the &amp;#8220;urn:inventory. The PowerShell dotted syntax would have included this element in its selection of department names, which may not be what was really intended. In order to be sure what you are processing XML in the namespace you intend with the PowerShell XML object model you really have to use the Item method at every level of the XML hierarchy. Your mileage may vary, but using XPath to select items from an XML file will in general be easier than using the PowerShell object model of XML and more capable.&lt;/P&gt;
&lt;P&gt;An XPath LocationPath can be thought of as a filter; You use it to filter out the parts of the document you are not interested in. This filter can be about as fine-grained as you would like. For example what if we wanted the department names on the 3rd floor?&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xitems C:\Demos\StockNS.xml `
   "p:GroceryList/i:Stock[loc:Dept='3rd floor']/id:Dept/i:Name" `
   @{p="urn:prices";i="urn:inventory";id="urn:identity";loc="urn:location"} |
   select-object -property value -unique
Value
-----
Produce
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The LocationPath used by this script has a predicate in it, that only selects Stock elements that have a loc:Dept child element whose value is &amp;#8220;3rd floor&amp;#8221;.&lt;/P&gt;
&lt;P&gt;The xitems function is produces an XPath navigator, and it can also take a XPathNavigator as input. This means you can use the results of one xitems function as input to another. Here is an example using stock.xml, the file without the namespaces to reduce the clutter:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xitems C:\Demos\Stock.xml "GroceryList/Stock" |
  %{xitems $_ "Dept/Name"} | Select-Object -property value -unique
Value
-----
Produce
Meat

PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This particular script is analogous to the first script that we presented in this article. We&amp;#8217;ve broken the selection into two pipeline segments just to show that the second segment could use the output of the first as input. The first pipeline segment pipes an XPathNavigator into the second pipeline segment which uses that XPathNavigator as input to another xitems function.&lt;/P&gt;
&lt;P&gt;From the &lt;A href="http://www.pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx"&gt;&lt;I&gt;Processing XML with PowerShell&lt;/I&gt;&lt;/A&gt; we know that xeval can also process an XPathNavigator, so the output of the xitems function can also be passed into the xeval function. Let&amp;#8217;s try that:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xitems C:\Demos\Stock.xml "GroceryList/Stock" |
  %{xeval $_ "string(Dept/Name)"} | Select-Object  -unique
Produce
Meat
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;In this example the second pipeline segment evaluates the result of the first pipeline segment. Note that in the third segment the Select-Object cmdlet is not using the -property value option. That is because the second segment is producing a string and a string does not have a value property.&lt;/P&gt;
&lt;P&gt;Lastly xitems is similar to xeval in that you can pass it an array of LocationPaths and it will apply all of them to an XML file.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xitems C:\Demos\Stock.xml "GroceryList/Stock/Dept/Area",
  "GroceryList/Stock/Dept/Name" | Group-object -property value
Count Name                      Group
----- ----                      -----
    1 Round                     {Area}
    2 Produce                   {Name, Name}
    1 Beef                      {Area}
    1 Meat                      {Name}
    1 Leaf                      {Area}
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This may seem a strange query, but it does show us, for example, that there are two Stock elements that have Name children whose value is &amp;#8220;Produce&amp;#8221;.&lt;/P&gt;
&lt;P&gt;So far we have seen the basics of using the xitems function and that it shares much in common with xeval. Let&amp;#8217;s now take a look at the implementation of xitems.&lt;/P&gt;
&lt;P&gt;First of all xitems is an alias for get-XSLT_XPathSelection. As the names implies this function is making an XPath selection.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;filter get-XSLT_XPathSelection
{
param($nav, [array]$expressions, [hashtable]$namespaces)
if($nav -is [string])
{
$nav = get-XSLT_XPathNavigator $nav
}
if($nav -isnot [System.Xml.XPath.XPathNavigator]) 
{ throw "String path or XPathNavigator required"}
$nm = get-XSLT_NamespaceManager $nav.NameTable $namespaces
$xpathExpression = "";
foreach($exp in $expressions)
{
if($xpathExpression -ne "")
{
$xpathExpression += " | ";
}
$xpathExpression += $exp
}
$nodes = $nav.Clone().Select($xpathExpression, $nm);
$nodes;
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The xitems function starts off the same as the xeval function that we looked at in the &lt;A href="http://www.pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx"&gt;&lt;I&gt;Processing XML with PowerShell&lt;/I&gt;&lt;/A&gt; article, it has a parameters an untyped $nav, an array and a hashtable. Just as xeval does, xitems converts a string to an XPathNavigator. It then builds a namespace manager and iterates through the expressions that were passed in, just like xeval does. In fact the only real differences from xeval is that xitems concatenates the selection expressions using the XPath alternate operator, &amp;#8220;|&amp;#8221; and uses Select instead of Evaluate on the XPathNavigator that was passed in.&lt;/P&gt;
&lt;P&gt;So in conclusion we can see that using xitems is really easier and more consistent than using the dotted syntax and Item method that the PowerShell XML object model uses, and is a lot more capable. Of course you will have to learn about XPath to fully exploit those capabilities, but it will be well worth you effort doing so.&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/43561.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>Processing XML with PowerShell</title><link>http://pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx</link><pubDate>Sat, 25 Nov 2006 11:25:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/42506.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/11/25/42506.aspx#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/42506.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/42506.aspx</trackback:ping><description>&lt;P&gt;There are a couple of powerful technologies for processing native XML, XPath and XSLT. People often avoid processing native XML but instead convert the XML to an object model in a language they are used to and do &amp;#8220;conventional&amp;#8221; programming on that model. Even PowerShell itself does this with its fairly straightforward dotted syntax for accessing parts of an XML document and of course .NET, web service technologies, and SQL Server have their own ways to morph XML into a familiar object model.&lt;/P&gt;
&lt;P&gt;There are probably a number reasons for this not the least of which is syntactic comfort&amp;#8230; with some practice you actually can drive nails with a screwdriver and then you only need to learn how to use one tool to build a house. XSLT itself is often criticized as being too verbose but that is not really the case. And lastly the programming models for XPath and XSLT are different than that used in languages like C# or VB.NET; They are much more like SQL in that you don&amp;#8217;t actually write a program but instead write a set of rules and throw data at them.&lt;/P&gt;
&lt;P&gt;However if you are going to bump into XML in your travels, and you can be pretty sure that you will, it is really worth your while to become comfortable with at least the basics of XPath and XSLT because that knowledge will make a lot of programming jobs a lot easier. Let&amp;#8217;s take a look at a simple example to see this. Here is a grocery list, XML-style, in the file groceries.xml&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;GroceryList&amp;gt;
&amp;lt;Item&amp;gt;
&amp;lt;Dept&amp;gt;Produce&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Orange&amp;lt;/Name&amp;gt;&amp;lt;Price&amp;gt;3.20&amp;lt;/Price&amp;gt;
&amp;lt;/Item&amp;gt;
&amp;lt;Item&amp;gt;
&amp;lt;Dept&amp;gt;Meat&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Steak&amp;lt;/Name&amp;gt;&amp;lt;Price&amp;gt;13.20&amp;lt;/Price&amp;gt;
&amp;lt;/Item&amp;gt;
&amp;lt;Item&amp;gt;
&amp;lt;Dept&amp;gt;Produce&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Lettuce&amp;lt;/Name&amp;gt;&amp;lt;Price&amp;gt;1.34&amp;lt;/Price&amp;gt;
&amp;lt;/Item&amp;gt;
&amp;lt;Item&amp;gt;
&amp;lt;Dept&amp;gt;Meat&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Ham&amp;lt;/Name&amp;gt;&amp;lt;Price&amp;gt;11.41&amp;lt;/Price&amp;gt;
&amp;lt;/Item&amp;gt;
&amp;lt;/GroceryList&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We can calculate the total of all of groceries using the PowerShell object model of XML with the following script;&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; [xml]$list = get-content .\groceries.xml
PS C:\Demos&amp;gt; $list.GroceryList.Item | &amp;amp;{begin {$sum=0}
 process{$sum += $_.Price} end {$sum}}  
29.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;There are other ways to do this in PowerShell, but all involve iterating through the items to produce a sum. It turns out there is a simple XPath expression that calculates sum of the prices of the items in the list:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;sum(GroceryList/Item/Price)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;In fact it would be kind of nice if we had a way to &amp;#8220;execute&amp;#8221; and XPath expression easily in PowerShell. How about this?&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xeval groceries.xml "sum(GroceryList/Item/Price)"
29.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This blog article is about processing XML using PowerShell and the typical sorts of things you run into when you do this. It uses some extension functions, xeval and xnav are their aliases, to do this processing. The xeval function is used to process an XML file using XPath expressions. The xnav function is used to turn literal XML into an XPathNavigator. Later blog articles will cover other ways to process XML using PowerShell.&lt;/P&gt;
&lt;P&gt;A script to build these functions and their associated aliases is in a file named XSLT.ps1. This file and the examples in this blog article are available at &lt;A href="http://www.pluralsight.com/dan/samples/ProcessingXMLPowershell.zip"&gt;http://www.pluralsight.com/dan/samples/ProcessingXMLPowershell.zip&lt;/A&gt;. These extension functions are not really any harder to use than the XML support built into PowerShell but are quite a bit more capable in what they can accomplish. After we look at using these extension functions we will look inside of XSLT.ps1 and see how it works.&lt;/P&gt;
&lt;P&gt;The XSLT.ps1 file actually has some other extension functions that are not discussed in this blog article but will be in a future one. &lt;/P&gt;
&lt;P&gt;In the first example of using xeval we just looked at, the first argument to the xeval function is the file path for the XML file you want to process. The second argument is the XPath expression you want evaluated. Of course to make good use of xeval you will have to be familiar with XPath. XPath is a W3 recommendation and is at &lt;A href="http://www.w3.org/TR/xpath"&gt;http://www.w3.org/TR/xpath&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;The XPath recommendation is certainly worth reading and contains many example of XPath expressions. Another good source to have at your side is &amp;#8220;Essential XML Quick Reference&amp;#8221; published by Addison-Wesley and written by Aaron Skonnard and Martin Gudgen. &lt;/P&gt;
&lt;P&gt;Let&amp;#8217;s start by looking at one of the issues you run into when working with XML. XML is often treated as though it were text and that is how PowerShell treats it. But XML is not plain ol&amp;#8217; text and the following examples will show that. We have another version of our xml grocery list in a file named GroceriesUC.xml. Let&amp;#8217;s use our PowerShell script to process it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; [xml]$list = get-content groceriesuc.xml
Cannot convert value "System.Object[]" to type 
"System.Xml.XmlDocument". 
Error: "Root element is missing."
At line:1 char:11
+ [xml]$list  &amp;lt;&amp;lt;&amp;lt;&amp;lt; = get-content GroceriesUC.xml
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Hmmmm, that generated an error. What&amp;#8217;s going on here?&lt;/P&gt;
&lt;P&gt;We often think of files as containing text, that is the characters we see on the printed page. But files don&amp;#8217;t contain text, the are just a sequence of bytes. When someone gives you a &amp;#8220;text&amp;#8221; file you must know how that text was encoded into a sequence of bytes in order to be able to read it. PowerShell gives us a little help here in that the get-content cmdlet lets you specify the encoding of the file if you know it, or &amp;#8220;unknown&amp;#8221; if you don&amp;#8217;t. Well we don&amp;#8217;t know the encoding of the file so let&amp;#8217;s tell PowerShell that the encoding is unknown and see what happens.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; [xml]$list = get-content GroceriesUC.xml -encoding unknown
Cannot convert value "????????????????????????????????????????????
?????????????????????????????????????????????????????????????????????
 to type "System.Xml.XmlDocument". Error: 
 "Data at the root level is invalid. Line 1, position 1."
At line:1 char:11
+ [xml]$list  &amp;lt;&amp;lt;&amp;lt;&amp;lt; = get-content GroceriesUC.xml -encoding unknown
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Looks like we are out of luck here too. It turns out the encoding of the file is UTF-16BE. That&amp;#8217;s a standard encoding used for XML files that is a sequence of words with the high-order byte of the word coming first. You might see it in XML that is generated on non-Intel compatible processors. Now that we know that we know actual encoding we can pass the information onto PowerShell.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; [xml]$list = get-content GroceriesUC.xml 
    -encoding BigEndianUnicode
PS C:\Demos&amp;gt; $list.GroceryList.Item | 
    &amp;amp;{begin {$sum=0} process{$sum += $_.Price} end {$sum}}
29.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Bottom line is when it comes to text unless you know the actual encoding you can&amp;#8217;t depend on being able to read it. Earlier we said that XML wasn&amp;#8217;t really text. To see what this means lets try that xeval function again on the GroceriesUC.xml file.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xeval GroceriesUC.xml "sum(GroceryList/Item/Price)"
29.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It works just fine and we don&amp;#8217;t have to tell it what the encoding is. The reason for this is a requirement of every XML processor, &lt;I&gt;i.e.&lt;/I&gt; a support library for XML such as the one in .NET that xeval uses, must be able to unambiguously figure out the encoding used in an XML file without any outside help. This is thought by many to be the key feature of XML and certainly is one of the reasons for its wide use today. It works so well that most people don&amp;#8217;t even know it is a feature! &lt;/P&gt;
&lt;P&gt;The built in processing in PowerShell using get-content makes a non-compliant XML processor. In some cases this isn&amp;#8217;t that important but you should keep in mind that in the general case it is not useful for processing XML. If you want do know the details of how this &amp;#8220;self-encoding&amp;#8221; in XML works there is an explanation of it in Appendix F of the W3 &lt;I&gt;Extensible Markup Language XML&lt;/I&gt; recommendation at &lt;A href="http://www.w3.org/TR/xml/"&gt;http://www.w3.org/TR/xml/&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;There is another issue that comes up when you deal with XML, namespaces. There are some who feel that namespaces are an unnecessary complication to XML, but they are important enough to have their own specification, &lt;I&gt;Namespaces in XML&lt;/I&gt; which is at &lt;A href="http://www.w3.org/TR/xml-names/"&gt;http://www.w3.org/TR/xml-names/&lt;/A&gt;. For those with an interest in such things the &lt;I&gt;Extensible Markup Language XML&lt;/I&gt; is really just a grammar with a little over 80 productions with lots of comments in it, and &lt;I&gt;Namespaces in XML&lt;/I&gt; just adds a few productions to that grammar. Regardless of how you feel about namespaces you will have to deal with them. Here is a different version of our grocery list. It is in the file named GroceriesNS.xml.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;GroceryList xmlns="urn:foo"
xmlns:r="urn:retail"
xmlns:w="urn:wholesale" &amp;gt;
&amp;lt;Stock&amp;gt;
&amp;lt;Dept&amp;gt;Produce&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Orange&amp;lt;/Name&amp;gt;
&amp;lt;w:Price&amp;gt;3.20&amp;lt;/w:Price&amp;gt;&amp;lt;r:Price&amp;gt;4.20&amp;lt;/w:Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock&amp;gt;
&amp;lt;Dept&amp;gt;Meat&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Steak&amp;lt;/Name&amp;gt;
&amp;lt;r:Price &amp;gt;14.20&amp;lt;/r:Price&amp;gt;&amp;lt;w:Price &amp;gt;13.20&amp;lt;/w:Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock&amp;gt;
&amp;lt;Dept&amp;gt;Produce&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Lettuce&amp;lt;/Name&amp;gt;
&amp;lt;w:Price&amp;gt;1.34&amp;lt;/w:Price&amp;gt;&amp;lt;r:Price&amp;gt;2.34&amp;lt;/r:Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock&amp;gt;
&amp;lt;Dept&amp;gt;Meat&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Ham&amp;lt;/Name&amp;gt;
&amp;lt;w:Price&amp;gt;11.41&amp;lt;/w:Price&amp;gt;&amp;lt;r:Price&amp;gt;14.41&amp;lt;/r:Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;/GroceryList&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This file is different from the Groceries.xml file in three ways. One is that it uses namespaces. Another is that it contains both a wholesale and a retail price for each item. It also uses Stock elements instead of Item elements; We will see why in a second. The Price elements are distinguished by their namespace, the ones prefixed with &amp;#8220;r&amp;#8221; are retail prices. The prices in the r:Price elements are the same as the corresponding Price elements in the Groceries.xml file. Let&amp;#8217;s use PowerShell&amp;#8217;s object model of XML to calculate the sum of the retail prices. PowerShell sees two Price elements under the Stock element, so it makes an array out of them. We will have pick which one to sum up. &lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; $list = get-content GroceriesNS.xml
PS C:\Demos&amp;gt; $list.GroceryList.Stock | 
    &amp;amp;{begin {$sum=0} process{$sum += $_.Price[1]} end {$sum}}
30.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The retail prices in the GroceriesNS.xml file are the same as the ones in the unqualified prices in the Groceries.xml file so we should get the same answer as before, but we don&amp;#8217;t. The problem we have run into is that Price elements are distinguished only by their namespace and not by there position in the file. Note that in the second Stock element in the file the wholesale price comes after the retail price. So we have to make sure that we pick the correct price element.&lt;/P&gt;
&lt;P&gt;To distinguish a Price element we have to use a ParameterizedProperty named Item that PowerShell adds to an XML element. In many cases you will find it difficult to process XML using the PowerShell object model if the XML contains any Item elements because PowerShell uses this name for the ParameterizedProperty it adds to XML elements. This is why we changed the name of the Item element to Stock. If we had not made this change we would not have been able to process this XML file using the PowerShell object model of XML.&lt;/P&gt;
&lt;P&gt;In any case the Item property allows us to specify both the name and the namespace of the element we want.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; $list = get-content GroceriesNS.xml
PS C:\Demos&amp;gt; $list.GroceryList.Stock | 
  %{$_.Item("Price", "urn:retail")} | 
  &amp;amp;{begin {$sum = 0} process {$sum += $_.get_InnerText()} end {$sum}}
29.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now we get the 29.15 just as we did when we processed the Groceries.xml file.&lt;/P&gt;
&lt;P&gt;Now let&amp;#8217;s do the same thing using xeval function.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xeval GroceriesNS.xml 
"sum(a:GroceryList/a:Stock/r:Price)" @{r="urn:retail";a="urn:foo"}
29.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;In this example xeval function has a third argument that is a dictionary that maps prefixes to the namespaces they represent in the XPath expression. You can see the GroceryList and Stock end up in the &amp;#8220;urn:foo&amp;#8221; namespace because of the &amp;#8220;a&amp;#8221; prefix and likewise Price ends up in the &amp;#8220;urn:retail&amp;#8221; namespace. Note that the prefix used in the XPath expression is not necessarily the same as that in the source XML file. There is no requirement the prefix used in an XPath expression be the same as that in the source XML file being processed; The key thing is that is specifies the proper namespace. Note that in the GroceriesNS.xml file the GroceryList and Stock element had no prefix but that the default namespace for the file was &amp;#8220;urn:foo&amp;#8221;.&lt;/P&gt;
&lt;P&gt;Let&amp;#8217;s look at some more things we can do with xeval. The second parameter of xeval may be an array of XPath expressions. xeval will evaluate each of these expressions. &lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xeval GroceriesNS.xml "sum(a:GroceryList/a:Stock/r:Price)",
    "count(a:GroceryList/a:Stock)" @{a="urn:foo";r="urn:retail"}
29.15
4
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we calculated the sum of the retail prices and number of Stock items. Note that this example makes use of the fact that in PowerShell the &amp;#8220;,&amp;#8221; operator makes an array of the arguments it joins. Let&amp;#8217;s carry this one step further.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xeval GroceriesNS.xml "sum(a:GroceryList/a:Stock/r:Price)",
    "count(a:GroceryList/a:Stock)",
    "sum(a:GroceryList/a:Stock/r:Price)
    div count(a:GroceryList/a:Stock)" @{a="urn:foo";r="urn:retail"}
29.15
4
7.2875
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here, beside the sum of the prices and the number of stock items, we calculate the average price of the stock items. The important point of these last few examples is that it is very common to calculate some value based on the content of an XML file. These calculations can be embedded in an XPath expression and you never have to &amp;#8220;read&amp;#8221;, &lt;I&gt;i.e.&lt;/I&gt; pull out and interpret parts of, the XML file to do this.&lt;/P&gt;
&lt;P&gt;You might think that all repeated a:GroceryList &lt;I&gt;etc.&lt;/I&gt; might be inefficient or at least is tedious. First of all it&amp;#8217;s not really inefficient at all to calculate a path multiple times in an XPath expression because the XPath engine that is evaluating these expression caches paths and reuses them when they appear again. As far as the tedium of typing them multiple times you can leverage PowerShell itself to simplify that.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; $s = "a:GroceryList/a:Stock"
PS C:\Demos&amp;gt; $p = "$s/r:Price"
PS C:\Demos&amp;gt; xeval GroceriesNS.xml "sum($p)",
    "count($s)",
    "sum($p) div count($s)" @{a="urn:foo";r="urn:retail"}
29.15
4
7.2875
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have made use of the fact that PowerShell will build a string out of a combination of literal text and variables. If the format of the XML file is pretty regular you can make the XPath expression used for the evaluation even more simple.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; xeval Groceries.xml "sum(//r:Price)" @{r="urn:retail"}
29.15
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Of course here again you need some knowledge of XPath to simplify things. The &amp;#8220;//&amp;#8221; part of the XPath expression in this case really means &amp;#8220;Find all the r:Price elements in the file.&amp;#8221;&lt;/P&gt;
&lt;P&gt;There is a hidden value in using XPath expressions to do calculations on an XML file; That expression can be used by anyone using any technology that implements XML support to do the same calculation on that file. In other words the XPath expression is a platform independent way of specifying how a calculation is done, it is not limited to PowerShell. &lt;/P&gt;
&lt;P&gt;Sometimes you will have a literal string for your xml instead of a file. You can&amp;#8217;t pass this directly to the xeval function because it will interpret that string as a file path and attempt to load a file.&lt;/P&gt;
&lt;P&gt;The implementation of xeval internally uses an XPathNavigator to process the XML that is passed to it. This blog article isn&amp;#8217;t going discuss the details of how XPathNavigator works, but xnav is an alias for a function that converts literal XML into an XPathNavigator. If the first parameter passed into xeval is an XPathNavigator it will use that navigator instead of interpreting it as a file path.&lt;/P&gt;
&lt;P&gt;Here is an example of processing literal XML.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; $nav = xnav "&amp;lt;Stock&amp;gt;&amp;lt;sku&amp;gt;ee-44&amp;lt;/sku&amp;gt;&amp;lt;/Stock&amp;gt;"
PS C:\Demos&amp;gt; xeval $nav "string(//sku)"
ee-44
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This example begins by using the xnav function to make an XPathNavigator out of some literal XML. This XPathNavigator is passed into the eval function. The XPath expression passed to xeval pulls out the stockroom unit from the literal XML.&lt;/P&gt;
&lt;P&gt;Using the pipeline in PowerShell is a great way process XML. There are a number of grocery files with names like GroceriesNS1.xml, GroceriesNS2.xml and so on that we would like to process. We would like to calculate the value of each these files. This is what the GroceriesNS1.xml file looks like.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;GroceryList xmlns="urn:foo"
xmlns:r="urn:retail"
xmlns:w="urn:wholesale" 
ID = "A-24"
&amp;gt;
&amp;lt;Stock&amp;gt;
&amp;lt;Dept&amp;gt;Produce&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Orange&amp;lt;/Name&amp;gt;
&amp;lt;w:Price&amp;gt;114.20&amp;lt;/w:Price&amp;gt;&amp;lt;r:Price&amp;gt;3.41&amp;lt;/r:Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock&amp;gt;
&amp;lt;Dept&amp;gt;Meat&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Steak&amp;lt;/Name&amp;gt;
&amp;lt;r:Price &amp;gt;13.20&amp;lt;/r:Price&amp;gt;&amp;lt;w:Price &amp;gt;14.20&amp;lt;/w:Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;Stock&amp;gt;
&amp;lt;Dept&amp;gt;Produce&amp;lt;/Dept&amp;gt;&amp;lt;Name&amp;gt;Lettuce&amp;lt;/Name&amp;gt;
&amp;lt;w:Price&amp;gt;21.34&amp;lt;/w:Price&amp;gt;&amp;lt;r:Price&amp;gt;1.36&amp;lt;/r:Price&amp;gt;
&amp;lt;/Stock&amp;gt;
&amp;lt;/GroceryList&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;First of all it&amp;#8217;s straightforward to get the names of these files.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; get-childitem C:\Demos\* |
 ?{$_.Name -match "GroceriesNS\d+.xml"}
    Directory: Microsoft.PowerShell.Core\FileSystem::C:\Demos

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        11/25/2006  10:15 AM        438 GroceriesNS1.xml
-a---        11/25/2006  10:14 AM        324 GroceriesNS2.xml
-a---        11/25/2006  10:16 AM        438 GroceriesNS3.xml

PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that the GroceryList element has an attribute name ID that identifies that list. We want to include that ID in our results.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; get-childitem C:\Demos\* |
 ?{$_.Name -match "GroceriesNS\d+.xml"} |
 %{xeval "$_" "string(f:GroceryList/@ID)", "sum(//r:Price)" `
 @{f="urn:foo";r="urn:retail"} }
A-24
17.97
31
54.4
109
57.97
PS C:\Demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;In this example we pipe the file names into a script block that uses the xeval function. This uses XPath expressions to get both the ID of the GroceryList and the sum of its Price elements. Note the backtick and the end of the third line to insure the continuation of the command line.&lt;/P&gt;
&lt;P&gt;The output we get is ID followed by sum. We might like something that produces a single line per GroceryList. We could pipe these results into another script block that aggregated these results by the pair&amp;#8230; or we could use XPath to do the same thing.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\Demos&amp;gt; get-childitem C:\BlogArts\ProcessingXMLPowerShell\* |
 ?{$_.Name -match "GroceriesNS\d+.xml"} |
 %{xeval "$_" "concat(string(f:GroceryList/@ID), ' : ', sum(//r:Price))" `
 @{f="urn:foo";r="urn:retail"} }
A-24 : 17.97
31 : 54.400000000000006
109 : 57.97
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we use the XPath concat function to produce a line per GroceryList report of the sum of the prices of each grocery list. You can produce some pretty fancy reports using just XPath expressions, but if they are much more complicated than the one in this example you will find it somewhat tedious to code them up. For more complicated reports XSLT is really a better choice and we will be looking at that in a later blog article. In any case this example has defined a report in terms of an XPath expression which anyone on any platform that implements XML can produce the same report. This example didn&amp;#8217;t &amp;#8220;code up&amp;#8221; a report it made a rule that defined how the report was to be produced.&lt;/P&gt;
&lt;P&gt;Now let&amp;#8217;s look at the implementation. We will start with the eval function.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;filter get-XSLT_XPathEvaluate
{
param($nav, [array]$computations, [hashtable]$namespaces)
if($nav -is [string])
{
$nav = get-XSLT_XPathNavigator $nav
}
if($nav -isnot [System.Xml.XPath.XPathNavigator]) 
  { throw "String file path or XPathNavigator required"}
$nm = get-XSLT_NamespaceManager $nav.NameTable $namespaces
foreach($n in $nav)
{
foreach($compute in $computations)
{
$n.Clone().Evaluate($compute, $nm)
}
}
}
set-alias xeval get-XSLT_XPathEvaluate
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The xeval function uses three parameters. The first is a string or an XPathNavigator, the second is an array of XPath expressions, and the last is a dictionary of namespace mappings. It tests the first parameter to see if it is a string. If it is it uses the get-XSLT&lt;EM&gt;XPathNavigator function to make an XPathNavigator from the file path. We will look at the get-XSLT&lt;/EM&gt;XPathNavigator function shortly.&lt;/P&gt;
&lt;P&gt;Next it checks to make sure that the $nav variable is in fact an XPathNavigator and throws an error if it isn&amp;#8217;t.&lt;/P&gt;
&lt;P&gt;In order to use namespace with an XPathNavigator you need a construct called an XmlNamespaceManager. This construct holds the mappings of prefixes to namespaces. Both XPathNavigators and XmlDocuments store their associated XML in a non-textual, binary form for efficiency. Internally another construct, a NameTable, maintains a mapping between the names of elements and attributes, and their internal representation. The XmlNamespaceManager uses this NameTable in its constructor so that it can have the same mapping of names to internal representation that the XPathNavigator does.&lt;/P&gt;
&lt;P&gt;Once the XmlNamespaceManager is constructed it is filled by get-XSLT_NamespaceManager function that we will look at shortly.&lt;/P&gt;
&lt;P&gt;To do the computations the xeval function iterates through the array of XPath expression that are passed in. It uses a clone of the XPathNavigator to execute the expression. The reason it uses a clone of the XPathNavigator is the XPathNavigator is really a cursor on the XML file and we want to leave that XPathNavigator in its original state for each execution of XPath expressions being processed.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;filter get-XSLT_XPathNavigator
{
param ($xml)
if($xml -is [string])
{
$xml = get-XSLT_XMLReader $xml;
$xml = get-XSLT_XPathDocument $xml
}
$nav = $xml.CreateNavigator();
$nav
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The get-XSLT_XPathNavigator uses the string passed into it as a file path. It starts by converting the file path into an XmlReader, then uses that XmlReader to make an XPathDocument, which in turn is used to make an XPathNavigator.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;filter get-XSLT_XMLReader
{
param ([string]$xmlFile)
[System.IO.FileStream]$fileStream = new-object System.IO.FileStream $xmlFile, 
    ([System.IO.FileMode]::Open),
([System.IO.FileAccess]::Read)
[System.Xml.XmlTextReader]$rdr = new-object System.Xml.XmlTextReader $fileStream
$rdr
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The get-XSLT_XMLReader function opens a FileStream using the string passed in as the path to the file. Note that it is not using a StreamReader which would convert the file to text, it is instead reading the raw bytes in the file. The FileStream is used to make an XmlTextReader. Again, dispite its name, an XmlTextReader does not read text, it reads bytes from the FileStream and because it is a complient XML processor it is completely capable of determining the encoding of the XML that is in the byte stream.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;filter get-XSLT_XPathDocument
{
param ([System.Xml.XmlReader]$xml)
$doc = new-object System.Xml.XPath.XPathDocument $xml;
$doc
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The get-XSLT_XPathDocument function uses an XmlReader to make an XPathDocument. An XPathDocument is, in effect, a readonly XmlDocument except that the only thing you can do with it is make an XPathNavigator out of it. If all you are going to do is read the content of an XML file and not modify it, and XPathDocument may be a better choice because it may be more efficient at processing XPath than the XmlDocument.Select method is.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;function get-XSLT_NamespaceManager 
([System.Xml.NameTable] $nameTable, [hashtable] $namespaces)
{
$nm = new-object System.Xml.XmlNamespaceManager $NameTable
foreach($key in $namespaces.keys)
{
$nm.AddNamespace($key, $namespaces.$key);
}
,$nm
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The get-XSLT_NamespaceManager has two inputs, a NameTable and a dictionary of namespace mapping. It starts by making an XmlNamespaceManager. It then iterates through the keys in the dictionary and uses the key and it associated value to add namespace mappings to the XmlNamespaceManager. Note that it uses the &amp;#8220;,&amp;#8221; operator when it returns the XmlNamespaceManager. The XmlNamespaceManager implements IEnumerable and returning it inside of an array prevents the XmlNamespace itself from being enumerated by PowerShell when it is returned, which is what we want.&lt;/P&gt;
&lt;P&gt;Lastly the get-XSLT_LiteralXPathNavigator function is used to make an XPathNavigator out of literal XML.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;filter get-XSLT_LiteralXPathNavigator
{
param ([string]$literalXml)
[xml]$xml = $literalXml;
$xml.CreateNavigator();
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This is a case where assuming the XML is in fact text is ok, becuase it is text and we use the PowerShell implementation of XML to get a navigator from the string.&lt;/P&gt;
&lt;P&gt;You probably have noticed the all of these functions have an &amp;#8220;XSLT_&amp;#8221; internal prefix in them but this blog article doesn&amp;#8217;t show anything about the use of XSLT. There is more comming in blog articles that follow this one&amp;#8230;&lt;/P&gt;
&lt;P&gt;So where are we at? The xeval function can handle a lot of the kinds of processing that is typically done with XML and has none of the limitations that the PowerShell implemenation of XML does. You do have to learn a bit about XPath, the references that were sited earlier are a good place to start and there are XPath tutorials all over the web. YMMV, but typically the best way to process XML is to process XML rather than turn it into an object model. It will be worth you effort at learning XPath&amp;#8230; after all it is easier to learn XPath than to learn Perl:-).&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/42506.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>PowerShell and XmlDocument</title><link>http://pluralsight.com/blogs/dan/archive/2006/10/30/41434.aspx</link><pubDate>Mon, 30 Oct 2006 15:09:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/10/30/41434.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/41434.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/10/30/41434.aspx#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/41434.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/41434.aspx</trackback:ping><description>&lt;P&gt;PowerShell has builtin support for XML, but the System.Xml namespace offers many additional capabilites for processing XML. This article looks at using System.Xml in PowerShell. This article assumes you know some of the basics of PowerShell programming and are familiar with the System.Xml namespace in .NET.&lt;/P&gt;
&lt;P&gt;First of all the [xml] data type variable in PowerShell is an instance of an XmlDocument. Typically an [xml] variable is used by assigning a variable to it. For example:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$xmldata = "&amp;lt;order&amp;gt;&amp;lt;line price='100' qty='3'&amp;gt;hammer&amp;lt;/line&amp;gt;&amp;lt;/order&amp;gt;"
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We are going to look at a different way to load XML into an [xml] variable. &lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata = new-object "System.Xml.XmlDocument"
PS C:\demos&amp;gt; $xmldata.LoadXml("&amp;lt;order&amp;gt;&amp;lt;line price='100' qty='3'&amp;gt;hammer&amp;lt;/line&amp;gt;&amp;lt;/order&amp;gt;")
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This way of initializing a variable produces the same result as assigning a string to an [xml] variable, but does not create a new instance of an XmlDocument. We don&amp;#8217;t want to create a new instance of an XmlDocument so we can leverage XPathNavigators and XPathExpressions but that will become evident later.&lt;/P&gt;
&lt;P&gt;You can get back the XML in text form by using the get_InnerXml() method.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata.get_InnerXml()
&amp;lt;order&amp;gt;&amp;lt;line price="100" qty="3"&amp;gt;hammer&amp;lt;/line&amp;gt;&amp;lt;/order&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;If you were programming in C# you would use the InnerXml property of the &lt;CODE&gt;$xmldata&lt;/CODE&gt; variable to retrieve the InnerXml, but you must use the underlying get_InnerXml method in PowerShell to do the same thing. This will be the case for the other properties in XmlDocument too, the properties are not available by just using their name as the are in C#.&lt;/P&gt;
&lt;P&gt;The root of an XML document is called the DocumentElement and for our document the name the root element is &amp;#8220;order&amp;#8221;. You can get a reference to it through the DocumentElement property and use its Name property to find its name.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata.get_DocumentElement().get_Name()
order
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we used the get_DocumentElement method to get the DocumentElement and the get_Name method to get its name.&lt;/P&gt;
&lt;P&gt;You can modify an XmlDocument by adding or removing XML nodes. A node is part of XML, for example an element is a node as is an attribute. Let&amp;#8217;s add another line to our order.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $line = $xmldata.CreateElement("line")
PS C:\demos&amp;gt; $line.SetAttribute("price", 23)
PS C:\demos&amp;gt; $line.SetAttribute("qty", 4)
PS C:\demos&amp;gt; $line.set_InnerText("nail")
PS C:\demos&amp;gt; $d =$xmldata.get_DocumentElement().AppendChild($line)
PS C:\demos&amp;gt; $xmldata.get_InnerXml()
&amp;lt;order&amp;gt;&amp;lt;line price="100" qty="3"&amp;gt;hammer&amp;lt;/line&amp;gt;&amp;lt;line price="23" qty="4"&amp;gt;nail&amp;lt;/line&amp;gt;&amp;lt;/order&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Elements for an XmlDocument are not created using a constructor. Instead a technique called a factory method is used to create them. This is typical of almost all XML processors on any platform. An XmlDocument contains factory methods to create the various kinds of nodes you find in an XmlDocument. To create a new line element we use the XmlDocument.CreateElement factory method.&lt;/P&gt;
&lt;P&gt;An XmlElement has a SetAttibute that is used to add attibutes to that element. We use the SetAttribute to add a &amp;#8220;price&amp;#8221; and &amp;#8220;qty&amp;#8221; attibute to the line element we created. SetAttribute is really a &lt;I&gt;shortcut&lt;/I&gt; method. We could use &lt;CODE&gt;CreateAttribute&lt;/CODE&gt; and &lt;CODE&gt;SetAttributeNode&lt;/CODE&gt; instead, but SetAttribute is more straightforward.&lt;/P&gt;
&lt;P&gt;The content of the new line element is set using the set_InnerText method. Again, if you were programming in C# you would assigning the InnerText property a value, but for an XmlDocument you must use the set_InnerText method. This will be true for the other assignable properties in XmlDocument too.&lt;/P&gt;
&lt;P&gt;Creating a element using a factory method does not add that element to the document. We use the AppendChild method of the DocumentElement of the document to add the line. Append child always makes the added element the last child. PrependElement will also add an element but will make it the first child element. AppendChild always returns a reference to the element that was appended. To prevent that returned value from &amp;#8220;leaking&amp;#8221; out of the script we capture it in the dummy &lt;CODE&gt;$d&lt;/CODE&gt; variable.&lt;/P&gt;
&lt;P&gt;Last we use the get_InnerXml method of the XmlDocument see that we have in fact added a new line element to the document.&lt;/P&gt;
&lt;P&gt;Now that we have a document let&amp;#8217;s do some processing of it. Each line has a &lt;CODE&gt;price&lt;/CODE&gt; and &lt;CODE&gt;qty&lt;/CODE&gt; attribute and the product of these two attributes is called the extended price. The value of an order is the sum of all of the extended prices in it. So let&amp;#8217;s calculate the value of an order. In this example we are going to use the native [xml] support built into PowerShell. &lt;/P&gt;
&lt;P&gt;To start with let&amp;#8217;s just calculate the extended prices.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata.order.line | %{$_.price * $_.qty}
100100100
23232323
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Somehow the results don&amp;#8217;t really look correct. Data in an XML document my be untyped or strongly typed. Untyped doesn&amp;#8217;t really mean the data doesn&amp;#8217;t have a type, it just means that each piece of data is considered to be a string even if it looks like a number. Strongly typed XML is produced by validating an XML document against an XML Schema. In this case the types of the pieces of data are known because they are defined in the XML Schema. Sometimes a validated XML document is called the Post Schema Validation Instance or PSVI.&lt;/P&gt;
&lt;P&gt;Our &lt;CODE&gt;$xmldata&lt;/CODE&gt; XML document is untyped so the price and quantity are considered to be strings. When the &amp;#8216;*&amp;#8217; operator is used with strings the string on its right is converted to an integer, or produces an error if it cannot be converted. The value of this integer is used to replicate and concatonate the string on the left of the operator. That is why we see &lt;CODE&gt;100&lt;/CODE&gt; repeated three times, the value of &lt;CODE&gt;qty&lt;/CODE&gt; for the first line is &amp;#8220;3&amp;#8221;.&lt;/P&gt;
&lt;P&gt;We have to cast the price to a double to get what we want.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata.order.line | %{[double]$_.price * $_.qty}
300
92
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now we can use the sum function example from my previous blog article, &lt;I&gt;PowerShell and XML and SQL Server&lt;/I&gt;, to find the value of the order.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; function sumOrder {
&amp;gt;&amp;gt; begin {$value = 0}
&amp;gt;&amp;gt; process { $value += [double]$_.price * $_.qty}
&amp;gt;&amp;gt; end {$value}
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
PS C:\demos&amp;gt; $xmldata.order.line | sumOrder
392
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We can use the SelectNodes method of XmlDocument to get the same result. It makes use of an XPath expression which is a bit more flexible, though more complicated, than the dotted syntax that PowerShell provides for [xml] variables.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata.SelectNodes("//line") | sumOrder
392
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The SelectNodes method returns a set of XML nodes from a document that meet some criterion specified by the XPath expression. This particular XPath expression returns all the line elements in the document. When a set of XML nodes is put into a pipeline PowerShell passes each one of the nodes one at a time into the pipe.&lt;/P&gt;
&lt;P&gt;One of the nice things about using XPath is that you can bury a lot of selection logic right into the XPath expression. What if we want to know the value of only the &lt;I&gt;expensive&lt;/I&gt; items in our order? Our definition of expensive is when the price is more than 99.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata.SelectNodes("//line[@price&amp;gt;99]") | sumOrder
300
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here our XPath expression has a predicate, &amp;#8220;[@price&amp;gt;99]&amp;#8221;, that filters out any lines whose value is 99 or less. The following is the equivalent using the XML capabilities built into PowerShell.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xmldata.order.line | ?{[double]$_.price -gt 99} | sumOrder
300
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Notice that in this case it was important to cast the price as a [double] otherwise PowerShell would have taken the filter to check to see if the price lexically sorted after the string &amp;#8220;99&amp;#8221;.&lt;/P&gt;
&lt;P&gt;An important thing about XPath is its universality. The XPath expression we have used in this example, technically called LocationPath, is a criterion for selection. Virtually every language and every platform supports XPath. You can pass the XPath expression we used in this example, &amp;#8220;line[@price&amp;gt;99}&amp;#8221;, to almost any other program and it will select the same lines for processing as we did in this example. &lt;/P&gt;
&lt;P&gt;You have to be careful reading XML. For example below is an XML file that is encoded as big endian UTF-16. You can&amp;#8217;t see the actual encoding on this page but you can download this test file from &lt;A href="http://www.pluralsight.com/dan/samples/PSXml.zip"&gt;http://www.pluralsight.com/dan/samples/PSXml.zip&lt;/A&gt; if you want to try it out.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;?xml version="1.0" encoding="UTF-16BE"?&amp;gt;
&amp;lt;Test/&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The get-content command is a way to read the content of a file. For example you might try to read the sample file into a builtin [xml] datatype in PowerShell like this:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$x = get-content "c:\demos\testdocs\test.xml"
Cannot convert value "System.Object[]" to type "System.Xml.XmlDocument". Error:
 "Root element is missing."
At line:1 char:8
+ [xml]$x  &amp;lt;&amp;lt;&amp;lt;&amp;lt; = get-content "c:\demos\testdocs\test.xml"
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;What PowerShell is doing here is reading in test.xml as string, then assigning that string to then &lt;CODE&gt;$x&lt;/CODE&gt; variable. Unfortunately when it does this it has to make a guess about the encoding of the file because I didn&amp;#8217;t tell it what it was and it guessed wrong. In fact if I just ask it to read the file and tell it I just don&amp;#8217;t know the encoding it will generate a lot of unknown characters because of an incorrect guess about the encoding of the file.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; get-content "c:\demos\testdocs\test.xml" -encoding Unknown
??????????????????????????????????????????????????????
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;However I happen to know the encoding for the file, as I said it big endian UTF-16, so I can do this:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$x = get-content "c:\demos\testdocs\test.xml" -encoding BigEndianUnicode
PS C:\demos&amp;gt; $x.get_InnerXml()
&amp;lt;?xml version="1.0" encoding="UTF-16BE"?&amp;gt;&amp;lt;Test /&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now we are able to read our big endian UTF-16 file. But this defeats one of the most important features of XML; You can read an XML file without knowing the encoding.&lt;/P&gt;
&lt;P&gt;Fortunately because PowerShell supports all of the .NET framework we can get around this problem and read any XML file that the underlying .NET Framework can handle without knowing its encoding.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $doc = new-object "System.Xml.XmlDocument"
PS C:\demos&amp;gt; $doc.Load($filePath)
PS C:\demos&amp;gt; $doc.get_InnerXml()
&amp;lt;?xml version="1.0" encoding="UTF-16BE"?&amp;gt;&amp;lt;Test /&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we initialize &lt;CODE&gt;$doc&lt;/CODE&gt; as an XmlDocument then use the Load function an XmlDocument to load in the file. The argument for Load is a string that can be either a file path or a URL. This is the recommended way to load an XML document into a variable because you shouldn&amp;#8217;t depend on knowing what encoding of an XML document is.&lt;/P&gt;
&lt;P&gt;Now that we can read XML lets process some XML files. Microsoft Word 2003 can be saved as XML. We have a few files that have been saved this way.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; dir c:\demos\testdocs\*.xml
    Directory: Microsoft.PowerShell.Core\FileSystem::C:\demos\testdocs
Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        10/30/2006  10:29 AM        108 betest.xml
-a---        10/30/2006   9:29 AM      24982 Test Document 1.xml
-a---        10/30/2006   9:33 AM      29195 Test Document 2.xml
-a---        10/30/2006   9:30 AM      25105 Test Document 3.xml
-a---        10/30/2006  10:05 AM        108 test.xml
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Actually some of the files in this directory are not Office documents, so we need a way to distinguish them. All Office XML files have on thing in common, they start with something called a processing instruction that looks like:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;&amp;lt;?mso-application progid="Word.Document"?&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Let&amp;#8217;s build a filter that will skip over the files that are not Word documents.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; dir c:\demos\testdocs\*.xml | ?{$x = new-object "System.Xml.XmlDocument";
&amp;gt;&amp;gt; $x.Load($_.FullName);
&amp;gt;&amp;gt; $x.SelectSingleNode("processing-instruction('mso-application')")}
&amp;gt;&amp;gt;
    Directory: Microsoft.PowerShell.Core\FileSystem::C:\demos\testdocs
Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        10/30/2006   4:29 PM      32448 Test Document 1.xml
-a---        10/30/2006   4:11 PM      29935 Test Document 2.xml
-a---        10/30/2006   9:30 AM      25105 Test Document 3.xml
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we use the full path name for each file to load an XmlDocument. Then we use the SelectSingleNode method of XmlDocument to see if we can find the processing instruction we are looking for. If the processing function isn&amp;#8217;t found then the file name is not passed out of the pipe so it does not get listed.&lt;/P&gt;
&lt;P&gt;The XPath expression we used was bit more complicated than the first one we tried. If you are interested in an interactive tool for working with XPath you can download Aaron Skonnard&amp;#8217;s XPath expression builder from &lt;A href="http://www.pluralsight.com/toolcontent/xpath-expression-builder-4.zip"&gt;http://www.pluralsight.com/toolcontent/xpath-expression-builder-4.zip&lt;/A&gt;. Also these test documents can be found at &lt;A href="http://www.pluralsight.com/dan/samples/PSXml.zip"&gt;http://www.pluralsight.com/dan/samples/PSXml.zip&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;Lastly we would like to show that these Word documents have been processed by PowerShell. After you open a document in Word if you go to File-&amp;gt;Properties-&amp;gt;Custom you will see that you can add custom properties of you own design to a word document. We would like to add a PowerShell custom property that indicates when the document was processed by PowerShell. These properties are embeded into the XML for the Word document.&lt;/P&gt;
&lt;P&gt;Another thing about Word documents that we haven&amp;#8217;t you looked at is that they make heavy use of XML namespaces. So before we try anything with a complete Word document let&amp;#8217;s look at simple document that has namespaces in it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$x = '&amp;lt;w:wordDocument
&amp;gt;&amp;gt; xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"
&amp;gt;&amp;gt; xmlns:o="urn:schemas-microsoft-com:office:office"&amp;gt;
&amp;gt;&amp;gt; &amp;lt;o:CustomDocumentProperties&amp;gt;
&amp;gt;&amp;gt; &amp;lt;/o:CustomDocumentProperties&amp;gt;
&amp;gt;&amp;gt;  &amp;lt;/w:wordDocument&amp;gt;'
&amp;gt;&amp;gt;
PS C:\demos&amp;gt; $x.get_InnerXml()
&amp;lt;w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"
xmlns:o="urn:schemas-microsoft-com:office:office"&amp;gt;&amp;lt;o:CustomDocumentProperties&amp;gt;&amp;lt;
/o:CustomDocumentProperties&amp;gt;&amp;lt;/w:wordDocument&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This document is a mini-Word document with all the things we don&amp;#8217;t care about stripped out of it.&lt;/P&gt;
&lt;P&gt;Custom properties for a Word document are contained in a CustomDocumentProperties element from the &amp;#8220;urn:schemas-microsoft-com:office:office&amp;#8221; namespace. If the Word document doesn&amp;#8217;t have any custom properties it will not have this element. So we will need a way to check to see if that element in the document. Let&amp;#8217;s test our mini-Word document to see verify we can find it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $custDoc = "/*/*[local-name()='CustomDocumentProperties' and namespace-uri()='urn:schemas-microsoft-com:office:office']"
icrosoft-com:office:office']
PS C:\demos&amp;gt; $x | ?{$_.SelectSingleNode($custDoc)}
wordDocument
------------
wordDocument
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have made an XPath expression and saved it into a variable so we can easily reuse it. It looks for an element whose name is CustomDocumentProperties and is in the office namespace. We can use it in a simple filter test and see that our test document can get through the filter.&lt;/P&gt;
&lt;P&gt;Next let&amp;#8217;s look at adding the CustomDocumentProperites if it is not there. First of all we will need an element to add.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $props = $x.CreateElement("CustomDocumentProperties", $custDoc)
PS C:\demos&amp;gt; $props.get_OuterXml()
&amp;lt;CustomDocumentProperties xmlns="/*/*[local-name()='CustomDocumentProperties' and namespace-uri()='urn:schemas-microsoft-com:office:office']" /&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;To create an element in a particular namespace we use the second parameter of the CreateElement method to specify the desired namespace. To check to see if we got what we wanted we use the OuterXml property&amp;#8230; there is no InnerXml for an element with no content. Let&amp;#8217;s make a test document without a CustomDocumentProperties and try adding this element.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $x2.get_InnerXml()
&amp;lt;w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"/&amp;gt;
PS C:\demos&amp;gt; if ($x2.SelectSingleNode($custDoc)){}else
&amp;gt;&amp;gt; {
&amp;gt;&amp;gt; $props = $x2.CreateElement("CustomDocumentProperties", "urn:schemas-microsoft-com:office:office")
&amp;gt;&amp;gt; $x2.get_DocumentElement().AppendChild($props)
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
PS C:\demos&amp;gt; $x2.get_InnerXml()
&amp;lt;w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"&amp;gt;
&amp;lt;CustomerDocumentProperties xmlns="urn:schemas-microsoft-com:office:office" /&amp;gt;&amp;lt;
/w:wordDocument&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we start off with a new XML document, &lt;CODE&gt;$x2&lt;/CODE&gt; that contains just a wordDocument. We use an &lt;CODE&gt;if&lt;/CODE&gt; construct to test the &lt;CODE&gt;$x2&lt;/CODE&gt; to see if it contains CustomDocumentProperties element. If if it does not we create on and add it. Then we check to make sure the element was added.&lt;/P&gt;
&lt;P&gt;This will be a useful for what we do next so let&amp;#8217;s save it as a function.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;function addCustomProps
{
$cust = $_.SelectSingleNode("/*/*[local-name()='CustomDocumentProperties' and namespace-uri()='urn:schemas-microsoft-com:office:office']")
if($cust){$cust}else{
$props = $_.CreateElement("CustomDocumentProperties", "urn:schemas-microsoft-com:office:office")
$_.get_DocumentElement().AppendChild($props)
}
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that the addCustomProps function always returns a CustomDocumentProperties.&lt;/P&gt;
&lt;P&gt;Now we have everthing we need to modify a Word document by adding a custom property to it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $filePath = "C:\demos\testdocs\test document 1.xml"
PS C:\demos&amp;gt; $doc = new-object "System.Xml.XmlDocument"
PS C:\demos&amp;gt; $doc.Load($filePath)
PS C:\demos&amp;gt; $prop = $doc.CreateElement("PowerShell", "urn:schemas-microsoft-com
:office:office")
PS C:\demos&amp;gt; $prop.SetAttribute("dt", "uuid:C2F41010-65B3-11d1-A29F-00AA00C14882
", "string")
string
PS C:\demos&amp;gt; $prop.set_InnerText([System.DateTime]::Now)
PS C:\demos&amp;gt; $doc | %{addCustomProps} | %{$_.AppendChild($prop)}
dt                                      #text
--                                      -----
string                                  10/30/2006 16:07:57

PS C:\demos&amp;gt;
PS C:\demos&amp;gt; $doc.Save($filePath)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We start off by setting the &lt;CODE&gt;$filePath&lt;/CODE&gt; variable to the path of a Word document. Next we load that Word document into the &lt;CODE&gt;$doc&lt;/CODE&gt; variable.&lt;/P&gt;
&lt;P&gt;We use the &lt;CODE&gt;$doc&lt;/CODE&gt; variable to create a PowerShell element, and fill it out with the current time. We also add a &lt;CODE&gt;dt&lt;/CODE&gt; attribute to specify that this is a string property and put the PowerShell element in the &amp;#8220;urn:schemas-microsoft.com:office:office&amp;#8221; namespace. Both of these are required for a custom property added to a Word document.&lt;/P&gt;
&lt;P&gt;Finally we pass the &lt;CODE&gt;$doc&lt;/CODE&gt; property through a pipeline our addCustomProps function. This function always returns the CustomDocumentProperty element so we can use the next segment of the pipeline to append our PowerShell property to it.&lt;/P&gt;
&lt;P&gt;If you now open the &amp;#8220;test document 1.xml&amp;#8221; file in Word and navigate to its custom properties you will see that is now has a PowerShell property.&lt;/P&gt;
&lt;P&gt;So we can use the full set of features available from the System.Xml namespace in .NET. The key to really making use of the is to become familiar with XPath. We really have just scratched the surface of its capabilities.&lt;/P&gt;
&lt;P&gt;Dan&lt;/P&gt;
&lt;P&gt;&lt;A href="mailto:dan@pluralsight.com"&gt;dan@pluralsight.com&lt;/A&gt;&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/41434.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>PowerShell and XML and SQL Server</title><link>http://pluralsight.com/blogs/dan/archive/2006/10/28/41337.aspx</link><pubDate>Sat, 28 Oct 2006 14:16:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/10/28/41337.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/41337.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/10/28/41337.aspx#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/41337.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/41337.aspx</trackback:ping><description>&lt;P&gt;PowerShell offers support for XML data directly though its [xml] datatype and this article is going to look at that.&lt;/P&gt;
&lt;P&gt;First of all to make use of the builtin [xml] datatype just prefix a variable name with [xml] when you assign something to it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$order = "&amp;lt;order customer='joe'&amp;gt;
&amp;gt;&amp;gt;         &amp;lt;line price='3.00' qty='4'&amp;gt;nut&amp;lt;/line&amp;gt;
&amp;gt;&amp;gt;         &amp;lt;line price='1.00' qty='3'&amp;gt;hammer&amp;lt;/line&amp;gt;
&amp;gt;&amp;gt;         &amp;lt;/order&amp;gt;"
&amp;gt;&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;At first glance this appears to be an ordinary assignment of a string to a variable. But it is doing more than that, it is checking the string to make sure that it is well-formed XML, &lt;I&gt;i.e.&lt;/I&gt; text that conforms to the XML specification. For example the following will produce an error&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$xdata = "&amp;lt;order &amp;gt;&amp;lt;line&amp;gt;stuff&amp;lt;/line&amp;gt;&amp;lt;/Order&amp;gt;"
Cannot convert value "&amp;lt;order &amp;gt;&amp;lt;line&amp;gt;stuff&amp;lt;/line&amp;gt;&amp;lt;Order&amp;gt;" to type "System.Xml.Xm
lDocument". Error: "Unexpected end of file has occurred. The following elements
 are not closed: Order, order. Line 1, position 34."
At line:1 char:12
+ [xml]$xdata  &amp;lt;&amp;lt;&amp;lt;&amp;lt; = "&amp;lt;order &amp;gt;&amp;lt;line&amp;gt;stuff&amp;lt;/line&amp;gt;&amp;lt;Order&amp;gt;"
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;because of a mis-match between the opening &lt;CODE&gt;&amp;lt;order&amp;gt;&lt;/CODE&gt; tag and the closing &lt;CODE&gt;&amp;lt;\Order&amp;gt;&lt;/CODE&gt; tag. So when you use an [xml] variable in PowerShell you can be sure that what it contains is XML.&lt;/P&gt;
&lt;P&gt;So $order is an [xml] variable, what can we do with it? One of the nice things about PowerShell is that if you are not sure what you can do with a varable you can always ask by piping the variable to the get-member function.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order | get-member

   TypeName: System.Xml.XmlDocument
Name                        MemberType            Definition
----                        ----------            ----------
ToString                    CodeMethod            static System.String XmlNo...
add_NodeChanged             Method                System.Void add_NodeChange...
add_NodeChanging            Method                System.Void add_NodeChangi...
add_NodeInserted            Method                System.Void add_NodeInsert...
add_NodeInserting           Method                System.Void add_NodeInsert...
add_NodeRemoved             Method                System.Void add_NodeRemove...
add_NodeRemoving            Method                System.Void add_NodeRemovi...
AppendChild                 Method                System.Xml.XmlNode AppendC...
Clone                       Method                System.Xml.XmlNode Clone()
CloneNode                   Method                System.Xml.XmlNode CloneNo...
CreateAttribute             Method                System.Xml.XmlAttribute Cr...
CreateCDataSection          Method                System.Xml.XmlCDataSection...
CreateComment               Method                System.Xml.XmlComment Crea...
CreateDocumentFragment      Method                System.Xml.XmlDocumentFrag...
CreateDocumentType          Method                System.Xml.XmlDocumentType...
CreateElement               Method                System.Xml.XmlElement Crea...
CreateEntityReference       Method                System.Xml.XmlEntityRefere...
CreateNavigator             Method                System.Xml.XPath.XPathNavi...
CreateNode                  Method                System.Xml.XmlNode CreateN...
CreateProcessingInstruction Method                System.Xml.XmlProcessingIn...
CreateSignificantWhitespace Method                System.Xml.XmlSignificantW...
CreateTextNode              Method                System.Xml.XmlText CreateT...
CreateWhitespace            Method                System.Xml.XmlWhitespace C...
CreateXmlDeclaration        Method                System.Xml.XmlDeclaration ...
Equals                      Method                System.Boolean Equals(Obje...
GetElementById              Method                System.Xml.XmlElement GetE...
GetElementsByTagName        Method                System.Xml.XmlNodeList Get...
GetEnumerator               Method                System.Collections.IEnumer...
GetHashCode                 Method                System.Int32 GetHashCode()
GetNamespaceOfPrefix        Method                System.String GetNamespace...
GetPrefixOfNamespace        Method                System.String GetPrefixOfN...
GetType                     Method                System.Type GetType()
get_Attributes              Method                System.Xml.XmlAttributeCol...
get_BaseURI                 Method                System.String get_BaseURI()
get_ChildNodes              Method                System.Xml.XmlNodeList get...
get_DocumentElement         Method                System.Xml.XmlElement get_...
get_DocumentType            Method                System.Xml.XmlDocumentType...
get_FirstChild              Method                System.Xml.XmlNode get_Fir...
get_HasChildNodes           Method                System.Boolean get_HasChil...
get_Implementation          Method                System.Xml.XmlImplementati...
get_InnerText               Method                System.String get_InnerText()
get_InnerXml                Method                System.String get_InnerXml()
get_IsReadOnly              Method                System.Boolean get_IsReadO...
get_Item                    Method                System.Xml.XmlElement get_...
get_LastChild               Method                System.Xml.XmlNode get_Las...
get_LocalName               Method                System.String get_LocalName()
get_Name                    Method                System.String get_Name()
get_NamespaceURI            Method                System.String get_Namespac...
get_NameTable               Method                System.Xml.XmlNameTable ge...
get_NextSibling             Method                System.Xml.XmlNode get_Nex...
get_NodeType                Method                System.Xml.XmlNodeType get...
get_OuterXml                Method                System.String get_OuterXml()
get_OwnerDocument           Method                System.Xml.XmlDocument get...
get_ParentNode              Method                System.Xml.XmlNode get_Par...
get_Prefix                  Method                System.String get_Prefix()
get_PreserveWhitespace      Method                System.Boolean get_Preserv...
get_PreviousSibling         Method                System.Xml.XmlNode get_Pre...
get_SchemaInfo              Method                System.Xml.Schema.IXmlSche...
get_Schemas                 Method                System.Xml.Schema.XmlSchem...
get_Value                   Method                System.String get_Value()
ImportNode                  Method                System.Xml.XmlNode ImportN...
InsertAfter                 Method                System.Xml.XmlNode InsertA...
InsertBefore                Method                System.Xml.XmlNode InsertB...
Load                        Method                System.Void Load(Stream in...
LoadXml                     Method                System.Void LoadXml(String...
Normalize                   Method                System.Void Normalize()
PrependChild                Method                System.Xml.XmlNode Prepend...
ReadNode                    Method                System.Xml.XmlNode ReadNod...
RemoveAll                   Method                System.Void RemoveAll()
RemoveChild                 Method                System.Xml.XmlNode RemoveC...
remove_NodeChanged          Method                System.Void remove_NodeCha...
remove_NodeChanging         Method                System.Void remove_NodeCha...
remove_NodeInserted         Method                System.Void remove_NodeIns...
remove_NodeInserting        Method                System.Void remove_NodeIns...
remove_NodeRemoved          Method                System.Void remove_NodeRem...
remove_NodeRemoving         Method                System.Void remove_NodeRem...
ReplaceChild                Method                System.Xml.XmlNode Replace...
Save                        Method                System.Void Save(String fi...
SelectNodes                 Method                System.Xml.XmlNodeList Sel...
SelectSingleNode            Method                System.Xml.XmlNode SelectS...
set_InnerText               Method                System.Void set_InnerText(...
set_InnerXml                Method                System.Void set_InnerXml(S...
set_Prefix                  Method                System.Void set_Prefix(Str...
set_PreserveWhitespace      Method                System.Void set_PreserveWh...
set_Schemas                 Method                System.Void set_Schemas(Xm...
set_Value                   Method                System.Void set_Value(Stri...
set_XmlResolver             Method                System.Void set_XmlResolve...
Supports                    Method                System.Boolean Supports(St...
Validate                    Method                System.Void Validate(Valid...
WriteContentTo              Method                System.Void WriteContentTo...
WriteTo                     Method                System.Void WriteTo(XmlWri...
Item                        ParameterizedProperty System.Xml.XmlElement Item...
order                       Property              System.Xml.XmlElement orde...

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Notice that an [xml] PowerShell variable is an .NET XmlDocument. So everything you know about an XmlDocument can be applied to a PowerShell [xml] variable. In a later article we will look at how you can leverage this fact, but for now we will stick to the XML support that is directly part of PowerShell.&lt;/P&gt;
&lt;P&gt;Look closely at the information returned by get-member&amp;#8230; notice there is a property named &amp;#8220;order&amp;#8221; of type XmlElement. If you lookup XmlDocument in MSDN you will not find an order property for it, this is something that PowerShell has added.&lt;/P&gt;
&lt;P&gt;The order property is in fact the root, or document, element for the &lt;CODE&gt;$order&lt;/CODE&gt; document. PowerShell has a simple path language for XML that makes use of the additional properties that PowerShell adds to XML elements. This language is not XPath but still very useful. The simplest thing you can do with an XML variable is ask what it contains:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order
order
-----
order

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It produces a table that shows the children of the &lt;CODE&gt;$order&lt;/CODE&gt; variable. It shows that the &lt;CODE&gt;$order&lt;/CODE&gt; variable has a single child named &amp;#8220;order&amp;#8221; whose value is &amp;#8220;order&amp;#8221;. Since we saw that order is a property of the $order variable we can append it with a &amp;#8220;.&amp;#8221; to that variable to see what that contains:.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order
customer                                line
--------                                ----
joe                                     {line, line}

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;When PowerShell displays the children of an element in at table, or list, it show both the elements and attributes of that element.&lt;/P&gt;
&lt;P&gt;If you prefer to see the children of an [xml] variable in list form you can pipe the results to the format-list function:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order | format-list

customer : joe
line     : {line, line}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we see that line has a number of &lt;CODE&gt;line&lt;/CODE&gt;s as children. &lt;/P&gt;
&lt;P&gt;We can extend this dotted property syntax further and find the value for customer:&lt;/P&gt;
&lt;P&gt;PS C:\demos&amp;gt; $order.order.customer joe PS C:\demos&amp;gt;&lt;/P&gt;
&lt;P&gt;We can tell that this is just a plain old value because the telltale &amp;#8220;&amp;#8212;&amp;#8212;-&amp;#8221; used to separate the names of properties from their values is not there. Likewise if we had used a list form the &lt;CODE&gt;name :&lt;/CODE&gt; is not there.&lt;/P&gt;
&lt;P&gt;Now let&amp;#8217;s use the dotted property syntax to see what the value of line is.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line
price                      qty                        #text
-----                      ---                        -----
3.00                       4                          nut
4.00                       2                          hammer
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we can see that PowerShell has output a two lines, one for each line element in the &lt;CODE&gt;order&lt;/CODE&gt; element. &lt;CODE&gt;price&lt;/CODE&gt; and &lt;CODE&gt;qty&lt;/CODE&gt; are attributes of the line and the special name &lt;CODE&gt;#text&lt;/CODE&gt; indicates that the line element contains some text. &lt;/P&gt;
&lt;P&gt;Since there are a number of &lt;CODE&gt;line&lt;/CODE&gt; elements we can refer to them individually using an array syntax.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line[0]
price                      qty                        #text
-----                      ---                        -----
3.00                       4                          nut

PS C:\demos&amp;gt; $order.order.line[1]
price                      qty                        #text
-----                      ---                        -----
4.00                       2                          hammer
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We can even ask how many line elements there are.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line.Length
2
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Under the covers &lt;CODE&gt;$order.order.line&lt;/CODE&gt; is a .NET array and we can do with it anything we might do with a .NET array. If we want the price of the second &lt;CODE&gt;line&lt;/CODE&gt; element&amp;#8230;&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line[1].price
4.00
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The dotted property syntax is actually a simple path language and is very similar to that used in data binding in ADO.NET, Windows Workflow, and Windows Presentation Framework. Now that we see the basics of this syntax lets see if we can do some calculations. Let&amp;#8217;s calculate the value of the order.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $value = 0
PS C:\demos&amp;gt; $order.order.line | %{$value += [double]$_.qty * $_.price}
PS C:\demos&amp;gt; $value
20
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that we had to cast the qty attribute to a double to force a numeric computation.&lt;/P&gt;
&lt;P&gt;First we create a &lt;CODE&gt;$value&lt;/CODE&gt; variable and initialize it to 0. Then we use the dotted property syntax to select the line elements in the &lt;CODE&gt;$order&lt;/CODE&gt; variable. &lt;CODE&gt;$order.order.line&lt;/CODE&gt; is actually a .NET array. When a .NET array is passed into a segment of a pipeline, as indicated by the &lt;CODE&gt;|&lt;/CODE&gt; symbol, each item in the array is passed into that segment one at a time for processing.&lt;/P&gt;
&lt;P&gt;The %{} syntax in the second segment of the pipeline says to execute what is between the braces once for each item that flows into the pipe. The &lt;CODE&gt;$_&lt;/CODE&gt; refers to the individual item that is flowing through that segment, in this case a line element. So as the line elements flow through the pipe the script between the braces calculates the extended price, &lt;I&gt;i.e.&lt;/I&gt; the &lt;CODE&gt;price&lt;/CODE&gt; times the &lt;CODE&gt;qty&lt;/CODE&gt;, for the line element and accumulates that product into the &lt;CODE&gt;$value&lt;/CODE&gt; element. Last we get the value of the &lt;CODE&gt;$value&lt;/CODE&gt; element.&lt;/P&gt;
&lt;P&gt;Next let&amp;#8217;s do this same calculation again, but be more &amp;#8220;pipeline&amp;#8221; oriented.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line | %{[double]$_.qty * $_.price} | &amp;amp;{begin{$v = 0}process{$v += $_}end{$v}}
20
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This is making use of an in-line function, the part inside of &lt;CODE&gt;&amp;amp;{}&lt;/CODE&gt;. It starts the same way the previous solution did, by passing the line elements into the pipeline. The second segment of the pipeline calculates the extended price for the line element and passes it onto the last segment.&lt;/P&gt;
&lt;P&gt;The last segement is the in-line function and consists of three parts. The first part is inside of &lt;CODE&gt;begin{}&lt;/CODE&gt; and is executed just once no matter how many elements pass through the pipeline and it initializes the &lt;CODE&gt;$v&lt;/CODE&gt; variable to 0.&lt;/P&gt;
&lt;P&gt;The second part of the function, the part inside of &lt;CODE&gt;process{}&lt;/CODE&gt;, is executed once on each item that passes through the pipeline. It just adds each of the extended prices it receives to the &lt;CODE&gt;$v&lt;/CODE&gt; variable.&lt;/P&gt;
&lt;P&gt;The last part of the function, the part inside of the &lt;CODE&gt;end{}&lt;/CODE&gt;, is also executed just once, but only after the process part is finished processing all of the items passing through the pipeline. All it does is refer to the &lt;CODE&gt;$v&lt;/CODE&gt; so it is returned.&lt;/P&gt;
&lt;P&gt;We can carry this even further. If we know we will be just summing up some numbers that pass though the pipeline we can change our in-line function into a named function and reuse it over and over.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; function sum {
&amp;gt;&amp;gt; begin{$v = 0}
&amp;gt;&amp;gt; process{$v += $_}
&amp;gt;&amp;gt; end{$v}
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
PS C:\demos&amp;gt; $order.order.line | %{[double]$_.qty * $_.price} | sum
20
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;So far our example has used literal XML, that is we have literally typed in the XML document we want to process. In real life you will probably have some XML files to process. There are a number of ways to read a file with PowerShell but the easiest it to use for our purposes is the &lt;CODE&gt;${}&lt;/CODE&gt; syntax. The file c:\demos\order.xml contains the same XML document we used to initialize $order in the previous examples.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$orders2 = ${c:\demos\order.xml}
PS C:\demos&amp;gt; $orders2
order
-----
order

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that the full path was specified.&lt;/P&gt;
&lt;P&gt;Another source of XML data is SQL Server. It uses a FOR XML clause in a SELECT statement to return the results of a query in the form of XML. For example the following query is made against a table that contains information about last names there were filled out on United States census forms.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;SELECT name, rank FROM LastNames FOR XML AUTO, ROOT('Names')
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The LastNames table contains the names from the census forms along with their rank, ranked by how many forms contained that name. Now we have to somehow use PowerShell to execute a query against SQL Server. Besides its builtin functionality PowerShell can use any functionality provided by a .NET class. So we will use ADO.NET to execute the query. The tricky part, as we shall see in shortly, is how to stick the results into an [xml] PowerShell variable.&lt;/P&gt;
&lt;P&gt;First we need a connection to SQL Server.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $conn = new-object System.Data.SqlClient.SqlConnection
PS C:\demos&amp;gt; $conn.ConnectionString = "server=.;integrated security=true;database=scratch"
PS C:\demos&amp;gt; $conn.Open()
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;To make the connection we use the PowerShell new-object function to create an instance of SqlConnection then fill out the connection string. Now we need a command.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $cmd = new-object System.Data.SqlClient.SqlCommand
PS C:\demos&amp;gt; $cmd.CommandText="SELECT name, rank FROM LastNames FOR XML AUTO,ROOT('Names')"
PS C:\demos&amp;gt; $cmd.Connection=$conn
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Similar to making the connection we make the command by creating an instance of a SqlCommand and fill out the CommandText and Connection.&lt;/P&gt;
&lt;P&gt;When ever a SqlCommand uses FOR XML it must be executed using ExecuteXmlReader, which returns its result in the form of an XmlReader.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xrdr = $cmd.ExecuteXmlReader()
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;If were were writing a program in C# or VB.NET then at this point we would use the XmlReader to Load an XmlDocument. Wait! Remember what we found out when we used get-member to find out what we could do with an [xml] PowerShell variable? We found out that is was an XmlDocument. So we can just use the Load method on an [xml] varible load it with the results of our query. These is one problem though&amp;#8230;&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$names.Load($xrdr)
You cannot call a method on a null-valued expression.
At line:1 char:18
+ [xml]$names.Load( &amp;lt;&amp;lt;&amp;lt;&amp;lt; $xrdr)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;A freshly created [xml] variable has not yet been initialized so we cannot use it to load anything. There are a couple of ways to intialize it. One is to just assign an XmlDocument to it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$names = New-Object System.Xml.XmlDocument
PS C:\demos&amp;gt; [xml]$names.Load($xrdr)
PS C:\demos&amp;gt; $names
Names
-----
Names
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have use new-object to create an XmlDocument and then assign it to an [xml] variable. After we have done that the Load method can be used to read the results of our FOR XML query into the &lt;CODE&gt;$order3&lt;/CODE&gt; variable. The we have PowerShell to output the &lt;CODE&gt;$name&lt;/CODE&gt; variable in tablular form. Here we can see it contains the expected &amp;#8216;Names&amp;#8221; root element.&lt;/P&gt;
&lt;P&gt;Another way to initialize the [xml] varible is to just put some XML into it, then write over it when the Load method is used.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$names = '&amp;lt;a/&amp;gt;'
PS C:\demos&amp;gt; $names.Load($xrdr)
PS C:\demos&amp;gt; $names
Names
-----
Names
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now that we have our Names XML document let&amp;#8217;s take a quick look inside of it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $names.Names
LastNames
---------
{SMITH, JOHNSON, WILLIAMS, JONES...}

PS C:\demos&amp;gt; $names.Names.LastNames.Length
88799
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we can see that there are 88799 names the document and the first few are SMITH,JOHNSON and so on.&lt;/P&gt;
&lt;P&gt;One last thing&amp;#8230; there is a bug in the RC2 PowerShell that hopefully will be fixed by the time the RTM version is released. Elements whose name is &amp;#8220;item&amp;#8221; will cause problems when the table or list formatter processes them.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$stuff = '&amp;lt;stuff&amp;gt;&amp;lt;item/&amp;gt;&amp;lt;item/&amp;gt;&amp;lt;/stuf
PS C:\demos&amp;gt; $stuff
stuff
-----
stuff

PS C:\demos&amp;gt; $stuff.stuff
format-default : The member "Item" is already present.
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;You will still be able to process &amp;#8220;item&amp;#8221; element otherwise, it is just an issue when format-table or format-list is used.&lt;/P&gt;
&lt;P&gt;We have seen the PowerShell has some pretty straightforward support for XML in its simple dotted path expressions. You can work with literal XML, XML from files and XML as it is returned from SQL Server in FOR XML queries. The PowerShell builtin path language that is very similar to that used in data binding which you may have already used. I didn&amp;#8217;t cover it in this article, but PowerShell also allows you to make use of all the XML capabilities built into .NET&amp;#8230; but that leaves me something to work on for the next article.&lt;/P&gt;
&lt;P&gt;Dan&lt;/P&gt;
&lt;P&gt;dan@pluralsight.com&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/41337.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>Comparing XML in SQL Server 2005</title><link>http://pluralsight.com/blogs/dan/archive/2006/09/01/36829.aspx</link><pubDate>Fri, 01 Sep 2006 07:37:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/09/01/36829.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/36829.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/09/01/36829.aspx#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/36829.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/36829.aspx</trackback:ping><description>&lt;H1&gt;Comparing XML in SQL Server 2005&lt;/H1&gt;
&lt;P&gt;Xml is not text so a literal compare of two xml documents may lead to a false negative, that is it may indicate that two documents are not equal when in fact they are. For example these two xml documents are the same: &lt;/P&gt;&lt;PRE&gt; &amp;lt;item x="1" y="2"/&amp;gt;
 
 
 &amp;lt;item y="2" x="1"/&amp;gt;
 &lt;/PRE&gt;
&lt;P&gt;because the order of attributes in xml is not signicant. SQL Server has not "built in" way to compare xml documents and in fact makes a point about the fact that it cannot compare xml documents. This is not a deficiency of SQL Server, it is just due to the fact that xml may not be treated as ordinary text. &lt;/P&gt;
&lt;P&gt;You can try to compare to xml documents by converting them to a string or a varbinary type, but this will lead to false negatives. For example: &lt;/P&gt;&lt;PRE&gt; DECLARE @x1 xml
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item x="1" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item y="2" x="1"/&amp;gt;'
 if CAST(@x1 AS VARBINARY(MAX)) = CAST(@x2 AS VARBINARY(MAX))
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 &lt;/PRE&gt;
&lt;P&gt;&lt;/P&gt;This prints out 'not equal' 
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;Things get more interesting if an xml schema is involved. For example: &lt;/P&gt;&lt;PRE&gt; CREATE XML SCHEMA COLLECTION Test AS
 '
 &amp;lt;xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
 targetNamespace = "urn:test"&amp;gt;
 &amp;lt;xs:element name="item"&amp;gt;
 &amp;lt;xs:complexType&amp;gt;
 &amp;lt;xs:attribute name="x" type="xs:int"/&amp;gt;
 &amp;lt;xs:attribute name="y" type="xs:int"/&amp;gt;
 &amp;lt;/xs:complexType&amp;gt;
 &amp;lt;/xs:element&amp;gt;
 &amp;lt;/xs:schema&amp;gt;'
 
 DECLARE @x1 xml(Test)
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 if CAST(@x1 AS VARBINARY(MAX)) = CAST(@x2 AS VARBINARY(MAX))
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 
 &lt;/PRE&gt;
&lt;P&gt;this prints out 'not equal' even though both @x1 and @x2 appear to be literally the same. This is due to the fact that SQL Server does not store literal form of the xml when it is strongly typed, it stores each of the simple types in the xml document in it's XML Schema canonical form. The conanical form of '00' for xs:int is '0'. So in this case the comparison is not equal becuase SQL Server is comparing '0' in the strongly typed @x1 to '00' in the weakly typed @x2. &lt;/P&gt;
&lt;P&gt;In order to compare two xml documents the must both be converted to a canonical form (http://www.w3.org/TR/2001/REC-xml-c14n-20010315). You can then do a byte by byte comparison of the two canonical forms to determine if they are the same according to the XML 1.1 recomendation, but will not take into account the canonical represenation of data types from the XML Schema recomendation. &lt;/P&gt;
&lt;P&gt;The XmlDsigC14NTransform class in the .NET Framwork can convert xml to canonical xml so that it can be compared. You can create a CLR user defined function that uses this class to do the comparison. For example: &lt;/P&gt;&lt;PRE&gt; public partial class UserDefinedFunctions
 {
  [Microsoft.SqlServer.Server.SqlFunction]
  public static SqlBoolean CompareXML(SqlXml x1, SqlXml x2)
  {
   if (x1.IsNull || x2.IsNull)
   {
    return SqlBoolean.Null;
   }
   XmlDocument xdoc1 = new XmlDocument();
   xdoc1.Load(x1.CreateReader());
   XmlDocument xdoc2 = new XmlDocument();
   xdoc2.Load(x2.CreateReader());
   XmlDsigC14NTransform xcanonical1 = new XmlDsigC14NTransform();
   xcanonical1.LoadInput(xdoc1);
   XmlDsigC14NTransform xcanonical2 = new XmlDsigC14NTransform();
   xcanonical2.LoadInput(xdoc2);
   // this returns the conanical form
   Stream s1 = (Stream)xcanonical1.GetOutput(typeof(Stream));
   Stream s2 = (Stream)xcanonical2.GetOutput(typeof(Stream));
   // do byte by byte compare
   if (s1.Length != s2.Length)
   {
    return new SqlBoolean(false);
   }
   for (Int32 index = 0; index &amp;lt; s1.Length; index++)
   {
    if (s1.ReadByte() != s2.ReadByte())
    {
     return new SqlBoolean(false);
    }
   }
   return new SqlBoolean(true);
  }
 };
 
 &lt;/PRE&gt;
&lt;P&gt;Then in T-SQL you can do the comparison: &lt;/P&gt;&lt;PRE&gt; DECLARE @x1 xml
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item x="1" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item y="2" x="1"/&amp;gt;'
 DECLARE @b bit
 set @b = dbo.CompareXml(@x1, @x2)
 IF @b = 1
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 &lt;/PRE&gt;
&lt;P&gt;and this will print out 'equal' as expected. &lt;/P&gt;
&lt;P&gt;Note that the assembly the contains the CompareXML function will have to be loaded with PERMISSION_SET = UNSAFE because XmlDsigC14NTransform.LoadInput may not be used from partially trusted assemblies. &lt;/P&gt;
&lt;P&gt;If the following comparison is done: &lt;/P&gt;&lt;PRE&gt; 
 DECLARE @x1 xml(Test)
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 if dbo.CompareXml(@x1,@x2) = 1
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 &lt;/PRE&gt;
&lt;P&gt;It will return 'not equal' because SQL Server does not store literal xml for a strongly typed xml variable. It stores strongly typed xml datatype in their XML Schema canonical form and this is '0' for '00' if the datatype is xs:int. Again this is expected because of the way that SQL Server treats strongly typed xml. &lt;/P&gt;Dan &lt;img src ="http://pluralsight.com/blogs/dan/aggbug/36829.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>What cost xml, SQL Server?</title><link>http://pluralsight.com/blogs/dan/archive/2006/08/06/32814.aspx</link><pubDate>Sun, 06 Aug 2006 15:36:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/08/06/32814.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/32814.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/08/06/32814.aspx#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/32814.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/32814.aspx</trackback:ping><description>&lt;P&gt;How much space does xml cost in SQL Server 2005? This question seems to come up a lot because xml has a lot of meta data in it and to top it off SQL Server saves the xml data type using UTF-16 encoding. This is, at least for xml that is mostly ascii type characters, up twice as much space as the encoding you typically see for xml, UTF-8. So if you just want to save xml in SQL Server then return it later you're better off storing as VARCHAR(MAX) or maybe VARBINARY(MAX), right? Maybe not, but that is what this article is going to look at...&lt;/P&gt;
&lt;P&gt;Even before we look at the space issues involved I'd like to mention that I think it is really being "too clever by half" to store xml as anything other than xml. Imagine if someone gave you a CSV file that looked like:&lt;/P&gt;
&lt;P&gt;pipe, 100&lt;BR&gt;rope, 250&lt;/P&gt;
&lt;P&gt;and and told you the first column was the name of an item and the second was the price in whole dollars, then asked you to make a table and import it into SQL Server. You could make a table that looked like:&lt;/P&gt;
&lt;P&gt;CREATE TABLE Stuff&lt;BR&gt;(&lt;BR&gt;name VARCHAR(MAX),&lt;BR&gt;price VARCHAR(MAX)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;Later when people when to use the table they could issue a query like this:&lt;/P&gt;
&lt;P&gt;SELECT name, CAST(price AS INT) FROM Stuff&lt;/P&gt;
&lt;P&gt;Of couse if one of the files you imported had a line like:&lt;/P&gt;
&lt;P&gt;hose, XVII&lt;/P&gt;
&lt;P&gt;the query would get an error at runtime when it ran. The problem here was that the data was stored as something that it wasn't. Storing xml as a string or binary is just like store an INT as text. When you insert something into an xml column SQL Server makes sure that is really is xml or rejects it, so you will not have any errors if you happen to make use of some of the xml functionality SQL Server 2005 provides. Even if you don't do make any use of the xml processing functionality in SQL Server, the client that reads the xml as text you've stored will get the error. It's usually best to catch errors as early as possible, especially if the client thinks your server made the error :-).&lt;/P&gt;
&lt;P&gt;OK, now lets look at how much space things really take up. To do this we are going to make four tables that store xml each in a different way, fill them up with a bunch of xml then see how big they are. You can down load the code for trying these experiments yourself from &lt;A href="http://www.pluralsight.com/dan/samples/whatcostxml.zip"&gt;http://www.pluralsight.com/dan/samples/whatcostxml.zip&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;-- save as binary&lt;BR&gt;CREATE TABLE XmlBinSizeTest&lt;BR&gt;(&lt;BR&gt;data VARBINARY(MAX)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;--save as varchar&lt;BR&gt;CREATE TABLE XmlTextSizeTest&lt;BR&gt;(&lt;BR&gt;data VARCHAR(MAX)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;--save save as xml with a schema&lt;BR&gt;CREATE TABLE XmlSchemaSizeTest&lt;BR&gt;(&lt;BR&gt;data xml(XmlSizeSchema)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;-- save as xml without a schema&lt;BR&gt;CREATE TABLE XmlSizeTest&lt;BR&gt;(&lt;BR&gt;data xml&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;Now we want to fill these tables up with a big walop of xml. When I need some static string or xml for testing I usually wrap it in a function so I don't have to put the literal text in my SQL expressions. &lt;/P&gt;
&lt;P&gt;CREATE &lt;BR&gt;--ALTER&lt;BR&gt;FUNCTION XmlSizeTestData4()&lt;BR&gt;RETURNS VARCHAR(MAX)&lt;BR&gt;AS&lt;BR&gt;BEGIN&lt;BR&gt;RETURN &lt;BR&gt;'&lt;EMPLOYEES XMLSchema-instance? 2001 www.w3.org http: xmlns:xsi="&lt;A href="&gt;http://www.w3.org/2001/XMLSchema-instance&lt;/A&gt;"&amp;gt;&lt;BR&gt;&amp;nbsp; &lt;E title="Production Technician - WC60" ModifiedDate="2004-07-31T00:00:00" rowguid="AAE1D04A-C237-4974-B4D5-935247737718" CurrentFlag="1" SickLeaveHours="30" VacationHours="21" SalariedFlag="0" HireDate="1996-07-31T00:00:00" Gender="M" MaritalStatus="M" BirthDate="1972-05-15T00:00:00" ManagerID="16" LoginID="adventure-works\guy1" ContactID="1209" NationalIDNumber="14417807" EmployeeID="1"&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;C ContactID="1209" mailto:guy1@adventure-works.com? EmailAddress="&lt;A href=" LastName="Gilbert" MiddleName="R" FirstName="Guy" NameStyle="0"&gt;guy1@adventure-works.com&lt;/A&gt;" EmailPromotion="0" Phone="320-555-0195" PasswordHash="1196EB7D5425B281CACDCDD2F60F52D9689D9E49" PasswordSalt="Lanmhoo=" rowguid="D366A33A-8EDE-42BD-BF79-3E7FB9713FE1" ModifiedDate="1996-07-24T00:00:00"/&amp;gt;&lt;BR&gt;&amp;nbsp; &lt;/E&gt;&lt;BR&gt;&lt;?-- a few hundred more E's here --&gt;&lt;BR&gt;&amp;nbsp;&lt;/EMPLOYEES&gt;'&lt;BR&gt;END &lt;/P&gt;
&lt;P&gt;Then a little batch to fill the tables:&lt;/P&gt;
&lt;P&gt;DECLARE @t VARCHAR(MAX)&lt;BR&gt;SET @t = dbo.XmlSizeTestData4();&lt;BR&gt;DECLARE @index INT&lt;BR&gt;SET @index = 100&lt;BR&gt;WHILE @index &amp;gt; 0&lt;BR&gt;BEGIN&lt;BR&gt;SET @index = @index - 1&lt;BR&gt;INSERT INTO XmlTextSizeTest VALUES (@t);&lt;BR&gt;INSERT INTO XmlSizeTest VALUES (@t);&lt;BR&gt;INSERT INTO XmlSchemaSizeTest VALUES (@t);&lt;BR&gt;INSERT INTO XmlBinSizeTest VALUES (CAST(@t AS VARBINARY(MAX)));&lt;BR&gt;END&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;We can use sys.allocation_units to find out how much space the tables take up. It takes a few joins to make this work off of table names...&lt;/P&gt;
&lt;P&gt;SELECT o.name AS table_name, au.type_desc, au.used_pages&lt;BR&gt;FROM sys.allocation_units AS au&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; JOIN sys.partitions AS p ON au.container_id = p.partition_id&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; JOIN sys.objects AS o ON p.object_id = o.object_id&lt;BR&gt;WHERE o.name in (N'XmlTextSizeTest',&lt;BR&gt;&amp;nbsp;N'XmlSizeTest',&lt;BR&gt;&amp;nbsp;N'XmlSchemaSizeTest',&lt;BR&gt;&amp;nbsp;N'XmlBinSizeTest')&lt;/P&gt;
&lt;P&gt;In SQL Server 2005 large objects, i.e. things like xml and VARCHAR(MAX) overflow in to supplementary pages outside of the pages for the table itself. This query tells us about both the pages used in the table and the extra overflow pages. Note that DBCC CHECKTABLE won't give you the right answer, because it won't tell you about the overflow pages. Running this query produces:&lt;/P&gt;
&lt;P&gt;table name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type_description&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; used pages&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2802&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2802&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4226&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;2907&lt;/P&gt;
&lt;P&gt;The numbers tell an interesting story. First of all no suprise, storing text as binary takes the same amount of space as storing it as VARCHAR(MAX). However storing xml without a schema takes only about 5% more space than storing it as VARCHAR(MAX) even though the xml is being stored as UTF-16, not single byte characters as VARCHAR does. How can this be?&lt;/P&gt;
&lt;P&gt;Let's use different xml data that really exacerbates the difference between storing xml as text and storing xml as xml. Here is another function that cobbles up some xml on the fly:&lt;/P&gt;
&lt;P&gt;CREATE &lt;BR&gt;FUNCTION XmlTestData1()&lt;BR&gt;RETURNS VARCHAR(MAX)&lt;BR&gt;AS&lt;BR&gt;BEGIN&lt;BR&gt;DECLARE @t VARCHAR(MAX)&lt;BR&gt;SET @t = CAST('&lt;?xml:namespace prefix = a /&gt;&lt;a:Simple  VARCHAR(MAX))&lt;BR AS ? xmlns:a="urn:stuff"&gt;+ REPLICATE(CAST(' ' AS VARCHAR(MAX)), 10000)&lt;BR&gt;+ '&amp;gt;abc&lt;/a:Simple &gt;'&lt;BR&gt;RETURN @t&lt;BR&gt;END&lt;/P&gt;
&lt;P&gt;It's basically inserting a lot of ignorable space into the the xml. A fragment of the xml it generates looks like:&lt;/P&gt;
&lt;P&gt;&lt;a:Simple                                                                                                                 &gt;&lt;/P&gt;
&lt;P&gt;Actually it has a lot more spaces than that. To try this next you should drop all the tables and recreate them, then use the batch that fill the tables, but change the source of the xml to dbo.XmlTestData1().&amp;nbsp; After you run that batch that finds the sizes you see something like:&lt;/P&gt;
&lt;P&gt;table name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type_description&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; used pages&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 252&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 252&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp; 3&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;It looks like saving this xml as text or binary takes up about two orders of magnitude more space than just saving it as xml!&lt;/P&gt;
&lt;P&gt;When SQL Server saves xml it need not save it as literal text, and it doen't. It doesn't have to save the the "pointies" and it doesn't have to save whitespace that doesn't matter. And most of the the time the whitespace between consecutive opening tags doesn't matter.&amp;nbsp; In other words SQL Server 2005 goes out of its way to efficiently store xml.&lt;/P&gt;
&lt;P&gt;So it not a slam dunk to figure out which way of storing xml takes up the least amount of space. The point here isn't that xml will always take about the same or less as a corresponding VARCHAR(MAX),&amp;nbsp; because sometimes the VARCHAR(MAX) or VARBINARY(MAX) will take up less space. But you will probably find that the difference isn't as great as you might suspect. In any case this is a database we are talking about, you should store xml as xml.&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;Dan&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;&amp;nbsp;&lt;BR&gt;&lt;/P&gt;&lt;/a:Simple                                                                                                                 &gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/32814.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>Evaluating String Arithmetic Expressions in SQL Server 2005</title><link>http://pluralsight.com/blogs/dan/archive/2006/07/27/32597.aspx</link><pubDate>Thu, 27 Jul 2006 10:50:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/07/27/32597.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/32597.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/07/27/32597.aspx#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/32597.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/32597.aspx</trackback:ping><description>&lt;P&gt;I saw a wish list blogged at http://omnibuzz-sql.blogspot.com/2006/07/next-version-of-sql-server-wish-list.html. It wanted a sql function that would evaluate an string arithmetic expression in the next version of SQL Server. It turns out that function is already available, sort of, in T-SQL on SQL Server 2005. Here is an example: &lt;/P&gt;&lt;PRE&gt; 
DECLARE @x xml
DECLARE @v FLOAT
SET @x = ''
SET @v = @x.value('(1 + 4) div 3', 'FLOAT')
SELECT @v
 ----------------
1.666666
&lt;/PRE&gt;
&lt;P&gt;Unfortunately the following will not work...&lt;/P&gt;&lt;PRE&gt;DECLARE @x xml
SET @x = ''
DECLARE @e VARCHAR(MAX)
DECLARE @v FLOAT
SET @e = '(1 + 4) div 3'
SET @v = @x.value(@v, 'FLOAT')
SELECT @v
&lt;/PRE&gt;
&lt;P&gt;... because value and other XQuery functions require literal XQuery expressions. But all is not lost!&lt;/P&gt;
&lt;P&gt;We can weave together a number of .NET technologies in SQL Server 2005 and make our own EvaluateArithmethicExpression scalar valued user defined function. All we have to do is add some basic XPath to&amp;nbsp;an XPathNavigator and regular expressions. XPath itself has builtin support for evaluating arithmetic expressions, albeit some of the operations are names rather than single characters, but a regular expression can clean that up. At the end of this article is a C# function that implements a CLR based user defined function that evaluates string arithmetic expressions. Here is an example of using this function: &lt;/P&gt;&lt;PRE&gt;&lt;FONT color=#0000ff size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SELECT&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; dbo&lt;/FONT&gt;&lt;FONT size=2&gt;.&lt;/FONT&gt;&lt;FONT size=2&gt;EvaluateArithmethicExpression&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;'(50.1234 * 1 + 2)/3'&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;DECLARE&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000 size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT color=#000000&gt;FLOAT&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000&gt;DECLARE&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @e &lt;/FONT&gt;&lt;FONT size=2&gt;VARCHAR&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;MAX&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @e &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT size=2&gt;'(50.1234 * 1 + 2)/3'&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; dbo&lt;/FONT&gt;&lt;FONT size=2&gt;.&lt;/FONT&gt;&lt;FONT size=2&gt;EvaluateArithmethicExpression&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;@e&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SELECT&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT color=#000000&gt; @f&lt;/FONT&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @e &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT size=2&gt;'((50.1234 * 1 + 2)/3) % 5'&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; dbo&lt;/FONT&gt;&lt;FONT size=2&gt;.&lt;/FONT&gt;&lt;FONT size=2&gt;EvaluateArithmethicExpression&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;@e&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SELECT&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT color=#000000&gt; @f&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT size=2&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;&lt;FONT size=2&gt;&lt;FONT size=1&gt;&lt;P&gt;&lt;FONT color=#000000&gt;----------------------&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;17.3744666666667&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;(1 row(s) affected)&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;----------------------&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;17.3744666666667&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;(1 row(s) affected)&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;----------------------&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;2.37446666666667&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;(1 row(s) affected)&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;
&lt;/PRE&gt;
&lt;P&gt;You can even access all the math functions in XPath itself...&lt;/P&gt;&lt;FONT color=#0000ff size=2&gt;
&lt;P&gt;&lt;FONT face="Courier New" color=#000000&gt;DECLARE&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#000000 size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New" color=#000000&gt;FLOAT&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; dbo&lt;/FONT&gt;&lt;FONT size=2&gt;.&lt;/FONT&gt;&lt;FONT size=2&gt;EvaluateArithmethicExpression&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;'ceiling((50.1234 * 1 + 2)/3) % 5'&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;
&lt;P&gt;&lt;FONT face="Courier New" color=#000000&gt;SELECT&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New" color=#000000&gt; @f&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;FONT size=2&gt;&lt;FONT size=1&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;----------------------&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=3&gt;3&lt;/FONT&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Here is the source code for the EvaluateArithmeticExpression&lt;/P&gt;&lt;PRE&gt;using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Xml;
using System.Xml.XPath;
using System.IO;
using System.Text.RegularExpressions;
public class ArithmeticCalculations
{
 // dummy doc for XPath eval's
 static readonly XPathDocument xpdoc;
 static ArithmeticCalculations()
 {
  StringReader sr = new StringReader("&lt;A&gt;");
  // doesn't matter whats in the doc
  // we just need something to call evaluate 