PowerShell XML

I'm putting some simple automation together for this website, to publish the static files regularly, and I had an itch to scratch that I thought was worth writing about.

The posts on the site all have the publish date encoded in their file name. The static file generator is based around some simple XML transforms, which is how I've re-skinned the site over the years. To be smarter about the use of publish dates, then, I decided I would have to do some date manipulation.

Changing templates and looking at results feels like too much work for the first few steps, wihch is to teach myself how dates work in XSLT. I need to convert them to and from strings, get the current date (or read it via a parameter) and do basic before/after comparisons. All things I should be able to do in isolation.

Enter PowerShell. Because it has access to .NET types, I can just prototype things very quickly. I need to get comfortable with dates, strings and XML.

Dates in PowerShell

First, how do dates work in PowerShell? These are the most straightforard, as you can simply get the current date with Get-Date and go from there.

PS > $d = Get-Date
PS > $d

Sunday, August 11, 2019 10:09:01 AM


PS > $d.GetType().FullName
System.DateTime
PS > $d.ToString("yyyy-MM-dd")
2019-08-11
PS > $tomorrow = $d.AddDays(1)
PS > $tomorrow

Monday, August 12, 2019 10:09:01 AM


PS > if ($d -lt $tomorrow) { echo "time works correctly" }
PS > $yesterday = [DateTime]::Parse("2019-08-10")
PS > $yesterday

Saturday, August 10, 2019 12:00:00 AM

Strings in PowerShell

Strings are reasonably straightforward, especially if you're using simple statements, but it's easy to make mistakes in some contexts. For example, this probably does what you expect if you're familiar with C#.

PS > $s1= "one"
PS > $s2= $s1 + " two"
PS > $s2
one two
    

But this is surprising.

PS > Write-Host $s1 + " two"
one +  two

What you see here is that Write-Host takes an array of objects as its input, and PowerShell sees this as a variable, a "+" string, and the " two" literal.

XML in PowerShell

There are a couple of nice built-in utilities for working with XML in PowerShell. Convert-To-Xml takes an object and serializes into a document, then returns it as a document object or a string. This gets very interesting with the ability to create objects with custom types on the fly, and with the ability to round-trip objects, but I'm looking for something more straightforward here.

To create a simple XML document, we can do use the New-Object command. This gives us access to the .NET object model.

PS > $doc = New-Object -Type System.Xml.XmlDocument
PS > $doc.LoadXml("<root><e publish='2019-08-01'>content</e><e publish='2119-08-20'>future</e></root>")
PS > $doc.root.SelectNodes("e")

publish    #text
-------    -----
2019-08-01 content
2119-08-20 future

So now, we can create our own little navigator and evaluate XPath expressions here.

PS > $n = $doc.CreateNavigator()  
PS > $n.Evaluate("string(/root/e[1]/@publish)")
2019-08-01

Go play!

Putting everything together, and remembering that sadly XPath 1.0 doesn't deal with string ordering, only equality, we resort to turning dates into numerical representation.

PS > $todayStr = $d.ToString("yyyyMMdd")
PS > $todayStr
20190811

PS > $n.Evaluate("/root/e[translate(@publish, '-', '') < '$todayStr']")


NameTable        : System.Xml.NameTable
NodeType         : Element
LocalName        : e
NamespaceURI     :
Name             : e
Prefix           :
Value            : content
BaseURI          :
IsEmptyElement   : False
XmlLang          :
UnderlyingObject : e
HasAttributes    : True
HasChildren      : True
SchemaInfo       : System.Xml.XmlName
CanEdit          : True
IsNode           : True
XmlType          :
TypedValue       : content
ValueType        : System.String
ValueAsBoolean   :
ValueAsDateTime  :
ValueAsDouble    :
ValueAsInt       :
ValueAsLong      :
OuterXml         : <e publish="2019-08-01">content</e>
InnerXml         : content  

Enjoy!

Tags:  powershell

Home