0

I have an array of nested dictionary-style arrays, like $Events here, with a column of property names and a column of values:

PS > $Events[0].EventProperties

Name      #text
-------   -------
EventName ItBroke
Category  Bad
When      2020

For reference, the data like this that I use is usually from xml that looks like this, so maybe I could process this better?:

<Events>
  <EventProperties Name="EventName">ItBroke</EventProperties>
  <EventProperties Name="Category">Bad</EventProperties>
  <EventProperties Name="When">2020</EventProperties>
</Events>
<Events ... />

I want to convert the nested EventProperties member arrays to objects instead, with properties of Name =Name and Value =#text like below:

PS > $EventData[0]

EventName : ItBroke
Category  : Bad
When      : 2020

I'll answer with the way I do this currently, but it's just slow and scales poorly so I'm looking for help structuring this better. It feels like something simple that I don't know the correct terms for to find on my own.

2 Answers 2

2

You can optimize what you have already done by using hash tables to store property/value data, converting those hash tables into custom objects, and then avoiding the use of the inefficient += to expand an array.

$EventData = $Events | Foreach-Object {
    $hash = [ordered]@{}
    $_.EventProperties | Foreach-Object {
        $hash[$_.Name] = $_.innertext
    }
    [pscustomobject]$hash
}
$EventData

You could potentially look at PowerShell 7 and consider Foreach-Object -Parallel if you have a lot of EventProperties nodes.

All of the looping is required because you want to convert attribute names to properties. The [xml] accelerator converts element (node) names to properties. I believe this behavior is typical for the other XML deserializers in PowerShell.


If you restructured your XML to have those attribute names be element names instead, only the conversion from XML to objects would be required:

# sample x.xml
<root>
<Events>
  <EventProperties>
    <EventName>ItBroke</EventName>
    <Category>Bad</Category>
    <When>2020</When>
  </EventProperties>
</Events>
<Events>
  <EventProperties>
    <EventName>ItBroke</EventName>
    <Category>Bad</Category>
    <When>2020</When>
  </EventProperties>
</Events>
</root>

# Conversion Code
$x = [xml](Get-Content x.xml)
$x.Root.Events.EventProperties

# Output
EventName Category When
--------- -------- ----
ItBroke   Bad      2020
ItBroke   Bad      2020
Sign up to request clarification or add additional context in comments.

1 Comment

Awesome, the construction using $hashtable[$newPropertyName] and the conversion to [pscustomobject] were both new to me, and work great! I do wish the data was formatted better, but sometimes it's not... This will be helpful for those times :)
0

The only way I know to do this is by individually creating objects, then adding member properties from the data to the object via a double ForEach loop

$EventData=@()                           # Create empty list object
# Iterate through events
Foreach ($event in $Events) {            # Iterate through events
    $object = New-Object PSObject        # Create new object for event data
    $event.EventProperties | Foreach {   # Iterate through data, and create new member properties
        Add-Member -InputObject $object `
            -MemberType NoteProperty `
            -name $_.Name `
            -Value $_.'#text'
    }
    $EventData += $object                # Add new object to result list
}
$EventData                               # Output result, matches example from question

This is slow, but dynamic enough to not have to worry about how many properties / whether all properties exist per event.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.