Reading XML

Hi all,

I’ve recently started using PowerShell to make my life easier in my job.

I often have to check Web.Config files on different servers and want to do this using PS as there can be 10 different App’s running on the same server all with their own Web.Config. Rather then just search for a string I would like to use the actual key and print out the value.

The XML files have multiple levels of nested elements, and after googling there seems to be so many different opinions on how to do this.

Can someone help point me in the right direction, maybe an e-book or video that covers how to get started.

Thanks

Paul

 

 

There’s isn’t a cmdlet to do it, but with a cast:

PS /Users/js> [xml]$xml = get-content test.xml                                   
PS /Users/js> $xml                                                               

Objs
----
Objs


PS /Users/js> $xml.objs                                                          

Version xmlns                                           Obj
------- -----                                           ---
1.1.0.1 http://schemas.microsoft.com/powershell/2004/04 {Obj, Obj, Obj, Obj}

# if you want to save changes
PS /Users/js> $xml.save('test.xml')

Thanks for the reply, when I try your code it just opens the XML in IE.

Would I then be able to do something like

Write-Host $xml.object.value 

It should work. What are you running? It sounds like you’re just running ‘test.xml’.

No matter what you read, you are going to have to decide what works for you. Meaning, what you can grasp, understand and internalize for future needs.

There are lots of articles all over the web regarding using PS to create, read, update and delete XML files and or their content. As you’ve obviously already found, but don’t let that overwhelm you. Be specific in the task you are after, and attack it one step at a time. PS is natively XML heavy in many respects.

There are cmdlets specifically for dealing XML as well as leveraging the .Net namespace for it.

Get-Command -Name '*xml*'

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          ConvertTo-Xml                                      3.1.0.0    Microsoft.PowerShell.Utility
Cmdlet          Convert-XMLtoJSON                                  5.0.0.1    Sorlov.PowerShell
Cmdlet          Export-Clixml                                      3.1.0.0    Microsoft.PowerShell.Utility
Cmdlet          Import-Clixml                                      3.1.0.0    Microsoft.PowerShell.Utility
Cmdlet          Merge-XMLFile                                      5.0.0.1    Sorlov.PowerShell
Cmdlet          New-XSDfromXML                                     5.0.0.1    Sorlov.PowerShell
Cmdlet          Select-Xml                                         3.1.0.0    Microsoft.PowerShell.Utility

Of course using any of what is hear requires you know what you are after and use the correct resource.

Here are few examples I’ve passed on.

PowerShell Data Basics: XML - Simple Talk

•Accessing XML data in PowerShell ◦Accessing XML with XPath.
◦Accessing XML as Objects.
◦Comparison of XPath and Object Approaches

•Modifying or Creating XML Data ◦Adding XML Data
◦Using XML for Object Serialization

https://www.red-gate.com/simple-talk/sysadmin/powershell/powershell-data-basics-xml

Mastering everyday XML tasks in PowerShell https://www.powershellmagazine.com/2013/08/19/mastering-everyday-xml-tasks-in-powershell
The Scripting Wife Learns to Use PowerShell to Work with XML https://blogs.technet.microsoft.com/heyscriptingguy/2012/03/25/the-scripting-wife-learns-to-use-powershell-to-work-with-xml

As for videos, YouTube is there for that as well…
https://www.youtube.com/results?search_query=PowerShell+xml

There are also free and fee-oriented modules you can acquire:

PowerShell Gallery | Power-XML 1.0.0.4 https://www.powershellgallery.com/packages/Power-XML/1.0.0.4

PowerShell Gallery | Xml 7.0
https://www.powershellgallery.com/packages/Xml/7.0

XML PowerShell Cmdlets An easy-to-use set of PowerShell Cmdlets offering real-time access to XML data. https://www.cdata.com/drivers/xml/powershell

I don’t know of any cmdlet that would work like an “import-xml”. Import-clixml won’t work with a typical xml file.

You were correct, I totally forgot the Get-Content.

 

@postanote

Thank you very much for the links, I’ve not had a chance to go through all of them yet but that ‘Scripting Wife’ blog was a great starting point and exactly what I needed to get me going.

Following through the blog I’ve hit a bit of a snag. If i’m understanding this correctly from the online stuff I’ve read, the end result is being put into a hash table…I think, I’m looking for the value of DataPortalServer, so when I do $xml.configuration.appsettings.add.value[6] this gives me the information that I’m looking for. However, the index may not always be the same, so I want to address it by the name of the key.

I’ve tried .ContainsKey(‘DataPortalServer’), I’ve tried $xml.configuration.appsettings.add.value[‘DataPortalServer’] as well as several other things that I’ve googled but I just can’t get it to work.

Thank’s so much for what you’ve already provided.

 

Function QPConfigFiles{
[xml]$xml=get-content C:\test.xml
$xml.configuration.appsettings.add ('CslaDataPortalUrl')
}
QPConfigFiles
<configuration>
<appSettings>
<!-- Dataportal -->
<add key="SystemUser" value="" />
<add key="SystemPW" value="" />
<add key="Authentication" value="CSLA" />
<add key="Location" value="Client" />
<add key="ChangeMonitorIntervalMins" value="2" />
<add key="CslaDataPortalProxy" value="Csla.DataPortalClient.GenuineChannelsProxy, Csla3" />
<add key="CslaDataPortalUrl" value="ghttp://localhost:8753/Rawr/RemotingPortal.rem" />
<add key="DataPortalServer" value="ghttp://localhost:8753/Rawr/DataPortal.rem" />

</appSettings>

Paulie

Hi, I don’t see your latest reply. Unfortunately this forum can’t handle xml. You can make a gist of the xml and link to it here. (Maybe this forum should use the technet system?) Actually select-xml has some potential. But I don’t know xpath. Xpath runs deep. Xpath filters can be used with get-winevent as well.

xpath info: https://www.w3schools.com/xml/xml_xpath.asp

I was trying this. Both $a.node and $xml.objs are XmlElement types.

# Selects the first book element that is the child 
#  of the bookstore element
$a = select-xml /bookstore/book[1] xpath.xml
$a
$a.node
$a.node.gettype()


# Selects all the title elements (anywhere) that have a "lang" attribute 
#  with a value of "en"
(select-xml "//title[@lang='en']" xpath.xml).node

lang #text
---- -----
en   Everyday Italian
en   Harry Potter
en   XQuery Kick Start
en   Learning XML


[xml]$xml = get-content xpath.xml
$xml.gettype()
$xml.Objs
$xml.Objs.gettype()

# same thing
$xml = (select-xml / xpath.xml).node   

It’s a shame that I can’t see my own post to edit it :frowning:

https://gist.github.com/turtle9270/464c9687c705a0aa5cba3f2272db8638#file-gistfile1-txt

I’ve linked the gist above.

The links posted have been great, especially the scripting wife blog. From what I can see, im at a stage where it’s put the detail into a hast table.

$xml.configuration.appsettings.add.value[6]

This line works, but I want to address it by a name not by index number, $xml.configuration.appsettings.add.value[‘DataPortalServer’]

 

Something like that, but I can’t for the life of me get it to work.

Paulie

It’s not a hash table. You can do it this way:

$xml.configuration.appsettings.add | where key -eq DataPortalServer | 
  select -expand value 

ghttp://localhost:8753/Rawr/DataPortal.rem

Or using xpath, which is case sensitive. With select-xml, the node property has the resulting object.

(Select-Xml "/configuration/appSettings/add[@key='DataPortalServer']" example.xml).node.value  

ghttp://localhost:8753/Rawr/DataPortal.rem

That’s great. I’ve almost got what I’m looking for!

Function QPConfigFilesxpath{
$configfiles=@( 
@{
Name='QP'
cfgpath = 'C:\inetpub\Qp\Web.Config'
param1 = 'ApiURL'
param2='ApiUrl'
},
@{
Name='IdentServer'
cfgpath ='C:\inetpub\IdentityServer\Web.Config'
param1='CslaDataPortalUrl'
param2='CslaDataPortalUrl'
param3='CslaDataPortalUrl'
}
)

for($i=0; $i -lt $configfiles.Length; $i++)
{
#$b=$file.param2
#write-output $configfiles[$i].param2
$a=$xml.selectNodes("//add[@key=`"$configfiles[$i].param2`"]")
$a.value
write-host `n
}

#$configfiles[0].cfgpath
#[xml]$xml=get-content $configfiles[0].cfgpath


#$b=$configfiles[0].param1
#Write-Output $b
#$xml.SelectNodes("//add[@key='IdentityServerURL']")
#$a=$xml.selectNodes("//add[@key='$b']")
#$a.value
}
QPConfigFilesxpath

This issue seems to be with [pre]$a=$xml.selectNodes("//add[@key=`"$configfiles[$i].param2`"]")[pre]

The write-out line above it prints the right then when I uncomment it but it doesnt work on the selectnodes.

 

Paulie

That’s a difficult quoting situation. How about:

$key = $configfiles[$i].param2
# selectnodes() takes an xpath string
# '//add' means search for an add node anywhere
$a = $xml.selectNodes("//add[@key='$key']")

But I don’t see an ‘ApiUrl’ key in your xml. I guess it’s in a different xml. Also the bottom closing ‘< /configuration >’ tag is missing in the xml.

Unless you want to use the “$( )” method, but it gets more complex and less readable. “$object[$index]” or “$object.property” doesn’t work inside a string the way you would want.

$a = $xml.selectNodes("//add[@key=`"$($configfiles[$i].param2)`"]")