1

I have following xml file but don't know how to read it with Powershell, Anyone can help with? Thanks!

I need to get the url value from Powershell.

<o:OfficeConfig xmlns:o="urn:xxx:xxx:xxx">
<o:services>
<o:service o:name="xxxx">
<o:url>https://xxx.xxx</o:url>
</o:service>
</o:services>
</o:OfficeConfig>

Thanks in advance!

Wayne Yang
  • 9,016
  • 2
  • 20
  • 40
  • 2
    It looks like we have a [XY problem](http://meta.stackexchange.com/a/66378/248777): You're asking about _XML_, via a _file_, thinking that you need to convert a web-service's response to XML first. However, it looks like the response is JSON, which you should process such as such, and which `Invoke-RestMethod` would directly turn into an object graph (which @AdminOfThings' answer does indirectly). I suggest you accept an answer here based on the question _as asked_, and create a _new_ question that deals with your actual problem. – mklement0 Nov 05 '20 at 15:07
  • 1
    Yes, I found if it's a XML file, that works... Thanks @mklement0 – Wayne Yang Nov 05 '20 at 15:15

3 Answers3

3

You can take advantage of the fact that PowerShell's convenient, property-based adaptation of the XML DOM essentially ignores namespaces, allowing to you simply drill down to the element of interest by the unqualified element names:

([xml] (Get-Content -Raw file.xml)).OfficeConfig.services.service.url

By contrast, the XPath-based Select-Xml cmdlet is namespace-aware, and therefore requires explicit namespace handling - or a workaround via the local-name() function, as shown in Mathias R. Jessen's answer.

If you want to use proper namespace handling - which is ultimately more robust, but not always necessary - use the following:

(
  Select-Xml '//o:url' file.xml -Namespace @{ o='urn:schemas-microsoft-com:office:office' }
).Node.InnerText
  • Note the need to pass a hashtable (@{ ... }) that declares the namespace prefixes and URLs used, which is the prerequisite for being able to use the prefixes (o:, in this case) in the XPath query.

    • The prefix names need not match the ones in the original, as long as they're consistent with the -Namespace argument and are mapped to the original URLs.
  • Select-Xml returns wrapper objects around the matched System.Xml.XmlNode instances, so .Node is required to access the latter, and .InnerText then returns the node's text content.

    • As an aside: This need to access .Node is inconvenient, as the typical use case is to care about the XmlNode only; GitHub suggestion #13669 seeks to ease the pain via a
      -Raw switch that returs the XmlNode instances directly.
mklement0
  • 382,024
  • 64
  • 607
  • 775
1

You could use Select-Xml:

$rawXml = @'
<o:OfficeConfig xmlns:o="urn:schemas-microsoft-com:office:office">
<o:services>
<o:service o:name="GetFederationProvider">
<o:url>https://odc.officeapps.live.com/odc/emailhrd/getfederationprovider</o:url>
</o:service>
</o:services>
</o:OfficeConfig>
'@ 

$urlNode = $rawXml |Select-Xml -XPath '//*[local-name() = "url"]' |Select -Expand Node
$url = $urlNode.innerText

$url will now contain the string "https://odc.officeapps.live.com/odc/emailhrd/getfederationprovider"

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Hey @Mathias R. Jessen, Thanks for your quick answer. However, `Select-XML` seems not to work on my side. I still cannot get the value via this way. Is there anything I missed? – Wayne Yang Nov 05 '20 at 14:56
  • @WayneYang Remove the `ConvertTo-Xml` call – Mathias R. Jessen Nov 05 '20 at 14:57
  • Thanks, I tried to remove `ConvertTo-Xml`, but got this error: Select-Xml: Cannot convert value "{"o:OfficeConfig":{"@xmlns:o":"urn:schemas-microsoft-com:office:office","o:services":{"o:service":{"@o:name":"GetFederationProvider","o:url":"https://odc.officeapps.live.com/odc/emailhrd/getfederationprovider"}}}}" to type "System.Xml.XmlDocument". Error: "The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type." – Wayne Yang Nov 05 '20 at 15:02
  • 1
    @WayneYang That's JSON, not XML. Please update your original post with what you're actually doing – Mathias R. Jessen Nov 05 '20 at 15:19
1

Since you are returning json, you can just convert from json to a PowerShell object:

$configServiceUrl = "https://officeclient.microsoft.com/config16processed?rs=en-us&build=16.0.7612"
$headers = @{'Accept' = 'application/json'}
$getFederationProviderEndpoint = Invoke-WebRequest -Uri "$($configServiceUrl)&services=GetFederationProvider" -Headers $headers -Method GET

$obj = $getFederationProviderEndpoint.Content | ConvertFrom-Json
$obj.'o:officeconfig'.'o:services'.'o:service'.'o:url'
AdminOfThings
  • 23,946
  • 4
  • 17
  • 27
  • While you are probably correct (though I suspect an `Invoke-RestMethod` call could replace the `Invoke-WebRequest` / `ConvertFrom-Json` combo), the problem is that the _question_ is decidedly about something else - please see my comment on the question. – mklement0 Nov 05 '20 at 15:09
  • 1
    @WayneYang: Please, please ask a _new_ question and ask AdminOfThings to assist you _there_. Answering a different question than asked will only create confusion for future readers. – mklement0 Nov 05 '20 at 15:21
  • I figured it out. Thanks @AdminOfThings ! – Wayne Yang Nov 05 '20 at 15:28