Is there a way to create objects from a semi-structured text file using PowerShell?

Viewed 187

I have the following text file. I am trying to create PowerShell objects from this file.

Item: Car
In Stock: YES
Make: Ford
Model: Taurus
Color: Blue
Miles: 0
Item: Car
Miles: 0
Item: Truck
Item: SUV
Item: Car
In Stock: YES
Make: Honda
Model: Civic
Color: Red
Miles: 0

The 'Item' property is where each object would begin. The tricky part to this is that I am needing each object to have the same properties. If the property does not exist in the text file, then it gets the value "N/A". I also need to create a 'Found:' property to insert.

For example, this is the output I am trying to get once converted to PowerShell objects.

Item: Car
Found: YES
In Stock: YES
Make: Ford
Model: Taurus
Color: Blue
Miles: 0

Item: Car
Found: NO
In Stock: N/A
Make: N/A
Model: N/A
Color: N/A
Miles: 0

Item: Truck
Found: NO
In Stock: N/A
Make: N/A
Model: N/A
Color: N/A
Miles: N/A

Item: SUV
Found: NO
In Stock: N/A
Make: N/A
Model: N/A
Color: N/A
Miles: N/A

Item: Car
Found: YES
In Stock: YES
Make: Honda
Model: Civic
Color: Red
Miles: 0

You may be thinking, "This is output is ridiculous." Yes, yes it is. I'm just trying to work with what I got. :)

On to the attempted solutions..

So, what I have been trying to do is to insert these properties and values if they don't exist.

I start by reading the file into a list and then looping over the list. Then, if the element matches 'Item:', check the next element. If it doesn't match 'Miles:', then start inserting the needed properties.

[System.Collections.Generic.List[System.Object]]$Content = Get-Content file.txt

for ([int]$i = 0; $i -lt $Content.Count; $i++){
    if ($Content[$i] -like "Item:*"){
        if ($Content[$i+1] -like "Miles:*") {
            $Content.Insert($i+1, "Found: NO")
            $Content.Insert($i+2, "In Stock: N/A")
            $Content.Insert($i+3, "Make: N/A")
            $Content.Insert($i+4, "Model: N/A")
            $Content.Insert($i+5, "Color: N/A")
            $Content.Insert($i+6, "Miles: N/A")
        }
    }
}

This approach seems to work to get the correct list structure. Although, I fear I may be assuming too much with the inserts. From here I am still lost trying to get PowerShell objects as output.

1 Answers

Try the following (requires PowerShell (Core) 7+):

# Define a custom class with the desired properties and default values.
class Custom {
  [string] $Item = ''
  [string] $Found = 'NO'
  [string] ${In Stock}  = 'N/A'
  [string] $Make = 'N/A'
  [string] $Model = 'N/A'
  [string] $Color = 'N/A'
  [int] $Miles = 0
}

# * Split the file into paragraphs that start with an "Item:" line.
# * Convert each paragraph into a hashtable of key-value pairs
#   with ConvertFrom-StringData.
# * Construct an instance of the custom class from each hashtable.
# * Set the value of the .Found property based on which properties
#   were populated.
(Get-Content -Raw file.txt) -split '(?m)(?=^Item:)' -ne '' |
  ConvertFrom-StringData -Delimiter : |
    ForEach-Object {
      $obj = [Custom] $_; if ($obj.Make -ne 'N/A') { $obj.Found = 'YES' }; $obj 
    }

In Windows PowerShell 5.1, ConvertFrom-StringData has no -Delimiter parameter, so additional work is needed:Tip of the hat to Theo.

# Define a custom class with the desired properties and default values.
class Custom {
  [string] $Item = ''
  [string] $Found = 'NO'
  [string] ${In Stock}  = 'N/A'
  [string] $Make = 'N/A'
  [string] $Model = 'N/A'
  [string] $Color = 'N/A'
  [int] $Miles = 0
}

# * Split the file into paragraphs that start with an "Item:" line.
# * Convert each paragraph into a hashtable of key-value pairs
#   with ConvertFrom-StringData.
# * Construct an instance of the custom class from each hashtable.
# * Set the value of the .Found property based on which properties
#   were populated.
(Get-Content -Raw file.txt) -split '(?m)(?=^Item:)' -ne '' -replace '(?m)^(.+?):', '$1=' |
  ConvertFrom-StringData |
    ForEach-Object {
      $obj = [Custom] $_; if ($obj.Make -ne 'N/A') { $obj.Found = 'YES' }; $obj 
    }
Related