惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
T
The Blog of Author Tim Ferriss
H
Help Net Security
博客园 - 叶小钗
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
DataBreaches.Net
博客园 - 聂微东
A
About on SuperTechFans
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
Martin Fowler
Martin Fowler
博客园 - 【当耐特】
S
SegmentFault 最新的问题
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
B
Blog
Engineering at Meta
Engineering at Meta
V
V2EX
Hugging Face - Blog
Hugging Face - Blog

博客园 - Vincent Yang

Cannot load macro project error SQL Express - "Failed generate a user instance..." SQL Express 2008 x64 Integration with Visual Studio 2008 SP1 PowerShell Operators Crystal Reports .NET Error - "Access to report file denied. Another program may be using it." - Vincent Yang (转:)SharePoint Database Naming Standards List Types & List Internal ID available within MOSS 2007 Telerik: IIS7 & IIS 7.5 and ‘Telerik.Web.UI.WebResource.axd’ is missing in web config 64bit SQL Server issues : Connections to SQL Server files (*.mdf) require SQL Server Express 2005 to function properly 正式入住Windows 7 + XPM A Generic Singleton Form Provider for C# Extreme Programming: Do these 12 practices make perfect? Pick up the pace with extreme programming Testing an ASP.NET Web Service using PowerShell Testing SQL Stored Procedures using PowerShell Working with Collections of Objects using PowerShell Generating iCalender file using ASP.NET SQL Server Precision And Scale Problems (SQL Server 精度问题) Six Quick Crystal Reports Design Tips
Parsing XML Files with PowerShell
Vincent Yang · 2009-02-13 · via 博客园 - Vincent Yang

In the context of using Windows PowerShell for lightweight software test automation, one of the most common tasks you need to perform is parsing data from XML files. For example, you may want to extract test case input and expected result data from an XML test cases file, or you might want to pull out results data from an XML test results file. Compared to parsing a flat text file, parsing most XML files is a bit tricky because of XML's hierarchical structure. There are several approaches you can take when parsing XML with PowerShell. In general, the most flexible technique is to read the entire XML file into memory as an XmlDocument object and then use methods such as SelectNodes(), SelectSingleNode(), GetAttribute(), and get_InnerXml() to parse the object in memory. Let me demonstrate with typical example. Suppose you want to parse this dummy XML test case data file:

<?xml version="1.0" ?>
<testCases>

 <testCase id="001">
  <inputs>
    <arg1 optional="no">3</arg1>
    <arg2>4</arg2>
  </inputs>
  <expected>7</expected>
 </testCase>

 <testCase id="002">
  <inputs>
    <arg1 optional="yes">5</arg1>
    <arg2>6</arg2>
  </inputs>
  <expected>11</expected>
 </testCase>

</testCases>
 
The dummy file represents test case data for a hypothetical Sum() method. Listed below is a PowerShell script which parses the XML file and produces as output:

PS C:\XMLwithPowerShell> .\parseXML.ps1

Parsing file testCases.xml

Case ID = 001 Arg1 = 3 Optional = no Arg2 = 4 Expected value = 7
Case ID = 002 Arg1 = 5 Optional = yes Arg2 = 6 Expected value = 11

End parsing

The complete script is:

# parseXML.ps1

write-host "`nParsing file testCases.xml`n"
[System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
$file = resolve-path("testCases.xml")
$xd.load($file)

$nodelist = $xd.selectnodes("/testCases/testCase") # XPath is case sensitive
foreach ($testCaseNode in $nodelist) {
  $id = $testCaseNode.getAttribute("id")
  $inputsNode = $testCaseNode.selectSingleNode("inputs")
  $arg1 = $inputsNode.selectSingleNode("arg1").get_InnerXml()
  $optional = $inputsNode.selectSingleNode("arg1").getAttribute("optional")
  $arg2 = $inputsNode.selectSingleNode("arg2").get_InnerXml()
  $expected = $testCaseNode.selectSingleNode("expected").get_innerXml()
  #$expected = $testCaseNode.expected 
  write-host "Case ID = $id Arg1 = $arg1 Optional = $optional Arg2 = $arg2 Expected value = $expected"
}

write-host "`nEnd parsing`n"

The first three statements of the script load file testCases.xml into memory as an XmlDocument object:

[System.Xml.XmlDocument] $xd = new-object System.Xml.XmlDocument
$file = resolve-path("testCases.xml")
$xd.load($file)

I could have loaded the XML file in a single line like so:

[xml] $xd = get-content ".\testCases.xml"

Using the three-statement approach in the script has no technical advantage but is somewhat more readable by an engineer with C# coding experience. Next I fetch the all testCase nodes into memory:

$nodelist = $xd.selectnodes("/testCases/testCase")

The SelectNodes() method accepts an XPath string which is case-sensitive. With the testCase nodes now in memory I can iterate through each node with a foreach loop. Alternatively I could have iterated using a for loop with an index variable (say $i) in conjunction with the Item() method. For each node, I first fetch the test case ID attribute:

$id = $testCaseNode.getAttribute("id")

I use the GetAttribute() method of the XmlElement class. Interestingly I could have written this instead:

$id = $testCaseNode.id

This alternative illustrates an important point. In an effort to make parsing XML with PowerShell easier than with C# or VB.NET, the designers of PowerShell decided to directly expose attributes and values of XML elements in the form of properties. But since arbitrary XML data is available as properties, PowerShell does not expose standard .NET Framework properties (such as InnerXml) because there could be a name conflict. Note that PowerShell does expose standard .NET Framework methods such as GetAttribute(). Continuing in my script, next I grab the values of arg1:

$inputsNode = $testCaseNode.selectSingleNode("inputs")
$arg1 = $inputsNode.selectSingleNode("arg1").get_InnerXml()
$optional = $inputsNode.selectSingleNode("arg1").getAttribute("optional")

I use the SelectSingleNode() method to grab the single <input> node. Now instead of using the standard InnerXml property, which PowerShell does not expose, I use the underlying PowerShell get_InnerXml() method which corresponds to the non-exposed InnerXml property. OK, but just how did I know about this get_InnerXml() method? As with many PowerShell scripting tasks, before writing my script I had previously experimented by issuing interactive commands at the PowerShell prompt. For example, after interactively loading the XML file into memory (by typing the first three statements in my script), I typed commands such as:

> $nodelist = $xd.selectnodes("/testCases/testCase")
> $firstnode = $nodelist.item(0)
> $inputs = $firstnode.selectSingleNode("inputs")
> $arg1 = $inputs.selectSingleNode("arg1")
> $arg1 | get-member | more

Using the get-member cmdlet is the key to discovering exactly what properties and methods are available to an object. Anyway, the rest of the script should be reasonably self-explanatory because I use the same coding techniques. To summarize, although there are several ways to parse an XML file using PowerShell, a flexible approach is to use the XmlDocument class. After reading an XML file into memory as an XmlDocument object, you can select multiple nodes into a collection using the SelectNodes() method, grab a single node using the SelectSingleNode() method, retrieve an attribute using either the standard GetAttribute() method or the name of the attribute which PowerShell exposes as a property, and you can obtain an element value using the special get_InnerXml() PowerShell method.