Paul Selles

Computers and cats

Powershell Tip #1: Strict Mode XML Parsing Gotcha

I love how easy it is to parse XML with Powershell, but then I started scripting in Strict Mode and got hung-up on a little problem dealing with attributes.

I will revisit a revised cats.xml to use as an example xml file:

<?xml version="1.0" encoding="utf-8"?>
<Cats>
	<Cat Name="Wilson" Type="Tabby">
		<Property Name="Fur" Value="Coarse"/>
		<Property Name="Color" Value="Orange" />
		<Part Name="Paws">
			<Property Name="Claws" Value="Very sharp" />
		</Part>
		<Part Name="Nose">
			<Property Name="Cute" Value="true" />
		</Part>
	</Cat>
	<Cat Name="Winnie" Type="Short hair">
		<Property Name="Fur" Value="Soft"/>
		<Property Name="Color" Value="Black" />
		<Part Name="Paws">
			<Property Name="Polydactyl" Value="true" />
			<Property Name="Claws" Value="Sharp" />
		</Part>
		<Part Name="Nose">
			<Property Name="Cute" Value="true" />
		</Part>
	</Cat>
	<Cat Name="Luna">
		<Property Name="Fur" Value="Soft"/>
		<Property Name="Color" Value="Black" />
		<Part Name="Paws">
			<Property Name="Claws" Value="Trimmed" />
		</Part>
		<Part Name="Nose">
			<Property Name="Cute" Value="true" />
		</Part>
	</Cat>
</Cats>

Lets make a script that will retrieve the Name of a cat by their type:

# Get cat name by type
Param (
	[ValidateNotNullOrEmpty()]
	[String]$Type
)
[Xml]$Cats = (Get-Content -Path C:\temp\cats.xml)
($Cats.Cats.Cat | Where {$_.Type -match $Type}).Name

The script is nice and short and does what we expect. If the Type matches, it returns the Name of the cat (otherwise, we get nothing):

CatTypeResults

Now let’s try the script in Strict Mode:

# Get cat name by type
Param (
	[ValidateNotNullOrEmpty()]
	[String]$Type
)
Set-StrictMode -Version Latest
[Xml]$Cats = (Get-Content -Path C:\temp\cats.xml)
($Cats.Cats.Cat | Where {$_.Type -match $Type}).Name

If we try to run the script again we run into problems:

CatTypeResultsStrictMode

What did we do wrong? As it turns out there are two problems. The first is that the entry for Cat Luna does not have a Type attribute (if we re-ran the tests removing Luna then the script will pass). Secondly, we are expecting Powershell to interpret what we mean by the property Type. We want Type as an attribute name, but how does Powershell know that? This is sloppy scripting, but it works until we switch into Strict Mode.

In order to move forward, we need a better idea of the objects that we are playing with. So let’s get the members of $Cats.Cats.

SystemXmlXmlDocumentGetMember

We can see that this is an System.Xml.XmlElement[1]. We could look the class up or we can intuitively see what method on from the list above will help us clean up our code and test for the attribute Type:

# Get cat name by type
Param (
	[ValidateNotNullOrEmpty()]
	[String]$Type
)
[Xml]$Cats = (Get-Content -Path C:\temp\cats.xml)
$Cat = $Cats.Cats.Cat | Where {$_.GetAttribute('Type') -match $Type}
if ($Cat) { $Cat.GetAttribute('Name') }

Using GetAttribute on attribute Type will solve the first error. We solve our second error (when a not matching type is entered) by making sure that the object is not null before reading the attribute Name.

Paul

References

[1] XmlXElement Class. MSDN Library

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: