XML, XPath-Like Navigation, and Legacy Enterprise Data
Parse, navigate, query, modify, and safely save XML while understanding elements, attributes, XPath, namespaces, and enterprise use cases.
Learning objectives
- Explain why XML remains common in enterprise and build/test tooling.
- Load XML as a .NET XmlDocument.
- Navigate elements and attributes with property syntax and XPath.
- Handle XML namespaces with XmlNamespaceManager.
- Modify and save XML without regex-based text surgery.
- Transform a realistic test/config document into reusable PowerShell objects.
1. Why XML still matters
XML appears in MSBuild files, Windows and Java tooling, test reports such as JUnit, legacy APIs, package manifests, and enterprise integrations. XML is more verbose than JSON, but it provides explicit elements, attributes, namespaces, mixed content, and mature schema/tooling ecosystems.
An element is a named node such as <service>. An attribute is metadata written on an element such as enabled="true".
2. Load XML into a real XML document
PowerShell's [xml] type accelerator converts text into a .NET XmlDocument. Read the full file as one string before parsing when using Get-Content.
$xmlText = @'
<deployment environment="staging">
<service name="api" enabled="true">
<endpoint port="443">https://api.example.test</endpoint>
</service>
<service name="worker" enabled="false">
<endpoint port="5672">amqp://queue.example.test</endpoint>
</service>
</deployment>
'@
[xml]$doc = $xmlText
$doc.DocumentElement.Name
$doc.deployment.environment4. XPath is a query language for XML trees
XPath describes paths and predicates through an XML document. Start with simple absolute paths and attribute filters. Use it when property-style navigation becomes ambiguous or when configuration has repeated elements.
$node = $doc.SelectSingleNode('/deployment/service[@name="api"]/endpoint')
$node.InnerText
$node.GetAttribute('port')5. Namespaces prevent name collisions—and cause common beginner failures
An XML namespace associates element names with a URI. A document may show a default namespace without a visible prefix. XPath still needs a prefix mapped to that namespace; otherwise a query that looks correct can return no nodes.
[xml]$namespaced = @'
<report xmlns="urn:example:test-report">
<suite name="smoke"><case name="health" result="passed" /></suite>
</report>
'@
$ns = [System.Xml.XmlNamespaceManager]::new($namespaced.NameTable)
$ns.AddNamespace('r','urn:example:test-report')
$namespaced.SelectNodes('/r:report/r:suite/r:case', $ns) | Select-Object name,result6. Modify a lab document through nodes, not regex
Once parsed, modify attributes or nodes through the XML object model. This is safer than regex replacement because the parser understands escaping, element boundaries, and document structure.
$worker = $doc.SelectSingleNode('/deployment/service[@name="worker"]')
$worker.SetAttribute('enabled','true')
$worker.endpoint.SetAttribute('port','5671')
$doc.OuterXml7. Save carefully and keep changes inside a workspace
$lab = Join-Path ([System.IO.Path]::GetTempPath()) 'ps-ch11-xml'
New-Item -ItemType Directory -Path $lab -Force | Out-Null
$path = Join-Path $lab 'deployment.xml'
$doc.Save($path)
[xml]$roundTrip = Get-Content -Raw -LiteralPath $path
$roundTrip.deployment.service | Select-Object name,enabled8. A realistic test-report pattern
JUnit-style reports often contain suites and testcase elements, with failures nested under testcases. The exact schema varies, so inspect a sample before writing selectors.
[xml]$report = @'
<testsuite name="smoke" tests="2" failures="1">
<testcase classname="Health" name="api responds" time="0.12" />
<testcase classname="Health" name="worker responds" time="0.08">
<failure message="timeout">worker did not answer</failure>
</testcase>
</testsuite>
'@
$failed = $report.SelectNodes('/testsuite/testcase[failure]')
$failed | ForEach-Object {
[pscustomobject]@{ Test=$_.name; Message=$_.failure.message; Detail=$_.failure.InnerText }
}9. XML versus JSON: choose by contract
| Concern | XML | JSON |
|---|---|---|
| Nested data | Strong | Strong |
| Attributes + elements | Native distinction | Properties only |
| Namespaces | Built in | Not part of JSON |
| Human terseness | Verbose | Usually compact |
| Common REST usage | Still used, especially legacy/enterprise | Dominant for modern web APIs |
| Schema ecosystems | Very mature | JSON Schema ecosystem |
The consumer and existing ecosystem decide the format. Do not rewrite a working XML interface into JSON merely because JSON feels newer.
10. Lab: safely update a test configuration and verify it
$lab = Join-Path ([System.IO.Path]::GetTempPath()) 'ps-ch11-xml-lab'
New-Item -ItemType Directory -Path $lab -Force | Out-Null
$path = Join-Path $lab 'test-config.xml'
@'
<config>
<service name="api" port="8080" enabled="false" />
<service name="worker" port="9000" enabled="true" />
</config>
'@ | Set-Content -LiteralPath $path -Encoding utf8
[xml]$cfg = Get-Content -Raw -LiteralPath $path
$api = $cfg.SelectSingleNode('/config/service[@name="api"]')
if ($null -eq $api) { throw 'api service not found' }
$api.SetAttribute('port','8443')
$api.SetAttribute('enabled','true')
$cfg.Save($path)
[xml]$verify = Get-Content -Raw -LiteralPath $path
$verify.SelectSingleNode('/config/service[@name="api"]') | Select-Object name,port,enabled
Remove-Item -LiteralPath $lab -Recurse -ForceExpected observations
- The final
apinode hasport="8443"andenabled="true". - Reloading the saved file proves the change was written to disk rather than only changed in memory.
- Cleanup removes only the isolated temporary lab directory.
11. Verification checklist
- I can distinguish an element, attribute, text node, and namespace.
- I can explain why a default namespace requires a namespace mapping for XPath.
- I changed XML through the XML object model rather than regex.
- I reloaded the saved XML and verified the intended node before cleanup.
12. Common mistakes
- Using regex to edit structured XML.
- Ignoring namespaces when XPath returns no results.
- Confusing element text with attributes.
- Changing a file before verifying the target node exists.
- Assuming every XML document has the same schema because the file extension is the same.
13. Knowledge check
Question 1. What does [xml] create?
Question 2. Why can an XPath query return no results on a document that visibly contains the element?
Question 3. Why prefer the XML object model over regex replacement?
Question 4. What is an XML attribute?
port="443".Question 5. Should XML always be replaced by JSON?
14. Summary and next bridge
XML remains operationally relevant. Parse it, navigate explicit nodes, understand attributes and namespaces, modify the object model, and save only inside controlled paths. Next, CLIXML uses XML specifically to serialize PowerShell-oriented object snapshots and exposes important type-fidelity limits.
15. Authoritative references
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.