27

Note: I'm using ConvertTo-XML, and cannot use Export-Clixml.

I create a simple PSObject:

$a = New-Object PSObject -Property @{ Name='New' Server = $null Database = $null UserName = $null Password = $null } 

I then convert it into XML using ConvertTo-XML:

$b = $a | Convertto-XML -NoTypeInformation 

The XML looks like this:

<?xml version="1.0"?> <Objects> <Object> <Property Name="Password" /> <Property Name="Name">New</Property> <Property Name="Server" /> <Property Name="UserName" /> <Property Name="Database" /> </Object> </Objects> 

I'm having trouble figuring out the dot notation or XPath query to extract the attributes/elements and convert $b back to the original PSObject.

4 Answers 4

21

You can do this pretty easily with XPath. Although PowerShell usually makes working with XML pretty simple, in this case I think the format using strictly PowerShell syntax would be pretty gross.

filter XmlProperty([String]$Property) { $_.SelectSingleNode("/Objects/Object/Property[@Name='$Property']").InnerText } $Name = $b | Xmlproperty Name $Server = $b | XmlProperty Server # etc... 

EDIT: To generically do this for an XML document that contains one or more Object elements, you can do something like this:

function ConvertFrom-Xml($XML) { foreach ($Object in @($XML.Objects.Object)) { $PSObject = New-Object PSObject foreach ($Property in @($Object.Property)) { $PSObject | Add-Member NoteProperty $Property.Name $Property.InnerText } $PSObject } } ConvertFrom-Xml $b 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Josh. I Would like to convert any PSObject, but this is enough to get me started. I'm doing this because PSObject doesn't support .NET serialization.
I updated my answer with a more general way of going back to PSObject. However, it's not recursive so keep that in mind.
7

My variant with unlimited depth.

See examples.

function ConvertFrom-Xml { <# .SYNOPSIS Converts XML object to PSObject representation for further ConvertTo-Json transformation .EXAMPLE # JSON->XML $xml = ConvertTo-Xml (get-content 1.json | ConvertFrom-Json) -Depth 4 -NoTypeInformation -as String .EXAMPLE # XML->JSON ConvertFrom-Xml ([xml]($xml)).Objects.Object | ConvertTo-Json #> param([System.Xml.XmlElement]$Object) if (($Object -ne $null) -and ($Object.Property -ne $null)) { $PSObject = New-Object PSObject foreach ($Property in @($Object.Property)) { if ($Property.Property.Name -like 'Property') { $PSObject | Add-Member NoteProperty $Property.Name ($Property.Property | % {ConvertFrom-Xml $_}) } else { if ($Property.'#text' -ne $null) { $PSObject | Add-Member NoteProperty $Property.Name $Property.'#text' } else { if ($Property.Name -ne $null) { $PSObject | Add-Member NoteProperty $Property.Name (ConvertFrom-Xml $Property) } } } } $PSObject } } 

3 Comments

Why isn't this a normal built-in function??
when comparing to $null you should always put $null on the left hand side. learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/…
3

I usually parse xml to hash tables but using the convertto function I grabbed from here I adapted the function to convert to pscustom objects

function xmlNodeToPsCustomObject ($node){ $hash = @{} foreach($attribute in $node.attributes){ $hash.$($attribute.name) = $attribute.Value } $childNodesList = ($node.childnodes | ?{$_ -ne $null}).LocalName foreach($childnode in ($node.childnodes | ?{$_ -ne $null})){ if(($childNodesList | ?{$_ -eq $childnode.LocalName}).count -gt 1){ if(!($hash.$($childnode.LocalName))){ $hash.$($childnode.LocalName) += @() } if ($childnode.'#text' -ne $null) { $hash.$($childnode.LocalName) += $childnode.'#text' } $hash.$($childnode.LocalName) += xmlNodeToPsCustomObject($childnode) }else{ if ($childnode.'#text' -ne $null) { $hash.$($childnode.LocalName) = $childnode.'#text' }else{ $hash.$($childnode.LocalName) = xmlNodeToPsCustomObject($childnode) } } } return $hash | ConvertTo-PsCustomObjectFromHashtable } function ConvertTo-PsCustomObjectFromHashtable { param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [object[]]$hashtable ); begin { $i = 0; } process { foreach ($myHashtable in $hashtable) { if ($myHashtable.GetType().Name -eq 'hashtable') { $output = New-Object -TypeName PsObject; Add-Member -InputObject $output -MemberType ScriptMethod -Name AddNote -Value { Add-Member -InputObject $this -MemberType NoteProperty -Name $args[0] -Value $args[1]; }; $myHashtable.Keys | Sort-Object | % { $output.AddNote($_, $myHashtable.$_); } $output } else { Write-Warning "Index $i is not of type [hashtable]"; } $i += 1; } } } 

Comments

2

If I understand this correctly, cast the strings as [xml] type:

$xml = [xml]'<?xml version="1.0"?> <Objects> <Object> <Property Name="Password" /> <Property Name="Name">New</Property> <Property Name="Server" /> <Property Name="UserName" /> <Property Name="Database" /> </Object> </Objects>' $xml xml Objects --- ------- version="1.0" Objects 

2 Comments

How does one then expand into those listed properties? [0] seems to vaguely work, but I can't get any further, even by specifying the names in quotes.
@RokeJulianLockhart $xml.objects.object.property.name

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.