To juxtapose and complement the existing answers:
derloopkat's helpful answer is the direct solution to your problem: It corrects your Select-Xml call to make it namespace-aware, via a hashtable of namespace-prefix-to-URI mapping(s) passed to -Namespace, and shows that each element name in your XPath query must be namespace-qualified.
- As an aside:
- You don't need to load your XML text into an
[xml] (System.Xml.XmlDocument) instance first: Select-Xml has a -Content parameter that directly accepts XML text.
- In cases where you do need an
[xml] instance based on in-memory XML text, you can just cast the text (string) directly to [xml], e.g. [xml] '<foo>bar</foo>'
Darin's helpful answer shows a namespace-agnostic OO alternative that relies on PowerShell's adaptation of the [xml] DOM:
In essence, PowerShell allows you to treat any parsed [xml] document as an object graph that you can drill into using regular dot notation, because PowerShell surfaces XML (child) elements and XML attributes as namespace-less properties on each object (XML node) in the graph.
E.g., $xmlDocument.unattend.settings enumerates all <settings> child elements of the <unattend> root element; in other words: it is the equivalent of XPath /unattend/settings, without having to worry about namespaces.
jdweng's answer uses an alternative XML-parsing API, System.Linq.Xml.XDocument:
- While this API is newer than
[xml] (System.Xml.XmlDocument) and in many ways more convenient than the latter, it is important to note:
Both APIs are and will likely continue to be supported.
In the context of PowerShell, given the aforementioned convenient XML DOM adaptation based on [xml], and the fact that Select-Xml is also [xml]-based, it is usually simpler to stick with [xml]:
Case in point: jdweng's answer in essence manually recreates (in flattened form) the DOM adaptation of the input XML document that you get for free when you use [xml] (and, as an aside, does so in an inefficient fashion, due to repeated Add-Member calls and the unnecessary use of an [ArrayList] instance to manually create an output list).
With respect to XPath support, specifically, the two APIs offer very similar API surfaces, so there's also no advantage to using [System.Xml.Linq.XDocument] - see below.
However, when it comes to structural modifications of XML documents, the System.Linq.Xml.XDocument-based API is preferable even in PowerShell; see this answer for an example.
For the sake of completeness: Here's the equivalent [System.Xml.Linq.XDocument] XPath solution:
# Simplified input XML
# 'THE TEXT OF INTEREST.' is what should be extracted.
$xml = @"
<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<settings pass="offlineServicing"></settings>
<settings pass="windowsPE"></settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<InputLocale>0809:00000809</InputLocale>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<FirstLogonCommands>
THE TEXT OF INTEREST.
</FirstLogonCommands>
</component>
</settings>
</unattend>
"@
# Parse the XML text.
$doc = [System.Xml.Linq.XDocument]::Parse($xml)
# Create an XML namespace manager that declares the input XML's
# default namespace URI and associates it with a self-chosen prefix, 'dns'
# NOTE:
# * The analogous thing happens behind the scenes for [xml], when you pass
# a hashtable with prefix-to-URI mappings to Select-Xml -Namespace,
# as in derloopkat's answer.
$nsm = [System.Xml.XmlNamespaceManager]::new([System.Xml.NameTable]::new())
$nsm.AddNamespace('dns', 'urn:schemas-microsoft-com:unattend')
# Now base your XPath query on the self-chosen namespace prefix, making
# sure that it is used with each element name in the path.
[System.Xml.XPath.Extensions]::XPathSelectElement(
$doc,
'//dns:settings[@pass="oobeSystem"]/dns:component[@name="Microsoft-Windows-Shell-Setup"]/dns:FirstLogonCommands',
$nsm
).Value.Trim()
The above prints THE TEXT OF INTEREST., proving that the text content of the element of interest was successfully extracted.
$ns = @{dns="urn:schemas-microsoft-com:unattend"}; $items = Select-Xml -Xml $xml -XPath '//dns:item' -Namespace $ns. Could you give me a few extra pointers please? I've provided a complete xml layoutt in my question, but still I havenulloutput, so I don't see how the other question answers this (maybe it does, but I don't see how to implement it here).$items = Select-Xml -Xml $xml -XPath '//dns:settings/component/FirstLogonCommands' -Namespace $nsand some other variations. Still null. I don't see how the other question answers this, except in a vague way. I'm trying to extrapolate from that answer how to get to a solution, but all I have is null output.Select-Xml -Xml $xmlDocument -XPath '//dns:settings[@pass="oobeSystem"]/dns:component[@name="Microsoft-Windows-Shell-Setup"]/dns:FirstLogonCommands' -Namespace $ns