Cannot load an XML document with PowerShell

Tags: xml powershell

Somehow I can't read an XML file with very simple structure in my PowerShell script. The XML file is generated by "Microsoft Log Parser Toolkit" and I want to load its contents into a database.

The script is very simple:

$datalist=[xml] (gc ".\users.xml");
foreach ($ROW in $datalist.ROOT.ROW) {
        Write-host $ROW.CompName;

The expected output is


but nothing gets printed out. I have seen similar examples working (for instance here where they use PowerShell to parse outputs of stsadm -o enumsites.

What am I missing here?

Contents of the users.xml is simple, too:

<?xml version="1.0" encoding="ISO-10646-UCS-2" standalone="yes" ?>
 <!ELEMENT CompName (#PCDATA)>
 <!ELEMENT SoftwareName (#PCDATA)>
 <!ELEMENT ROW (CompName, SoftwareName)>
<ROOT DATE_CREATED="2009-12-30 10:44:23" CREATED_BY="Microsoft Log Parser V2.2">
  <SoftwareName>Adobe Reader 9.0</SoftwareName>
  <SoftwareName>CorelDraw Graphics Suite X4</SoftwareName>
By : naivists

This should solve it:

Select-Xml -Xml $datalist -XPath '//ROW' | % { $_.Node.CompName }

Edit: according to MediaAndMicrocode Where-Object should be used to filter out invalid values, but in this case it works without it.

Where lies the problem?

What is interesting:

poshdev >[62]: $x.ROOT | select -exp row
Select-Object : Property "row" cannot be found.
At line:1 char:17
+ $x.ROOT | select <<<<  -exp row
    + CategoryInfo          : InvalidArgument: (:PSObject) [Select-Object], PSArgumentException
    + FullyQualifiedErrorId : ExpandPropertyNotFound,Microsoft.PowerShell.Commands.SelectObjectCommand

CompName                                                                 SoftwareName
--------                                                                 ------------
User1                                                                    Adobe Reader 9.0
User2                                                                    Corel Photoshop 12

So if property can not be found, let's check its members:

$datalist.ROOT | gm

it shows that is a collection of string and XmlElement. Based on the knowledge this works as expected:


It means, that cast to [xml] created 2 items: string (probably from the DTD part) and xml. Other workaround is simply remove the DTD part from the xml.

By : stej

You don't have to remove the DTD part with Select-Xml, you can simply pipe it into a Where-Object. When you ask for all nodes with Select-Xml (Select-Xml //*), Select-Xml will automatically collect all of the namespaces:

Select-Xml -Xml $datalist -XPath '//*' |
    Where-Object { $_.Node.CompName } |
    Foreach-Object { $_.Node.CompName } 

You should also know that piping a file into Select-Xml is considerably faster that using Get-Content to read it. Get-Content reads line by line, and this can get very slow on large files. dir | Select-Xml will avoid reading the files line by line.

Hope this helps

This video can help you solving your question :)
By: admin