Using ConvertFrom-String with value that are not present in all sets of data

Viewed 680

I have a text file with data that are structured (similarly to Json) that I want to parse with ConvertFrom-String to extract part of the data. I cannot change how the data are structured in the file so I have to deal with them the way they are.

Here's a sample of the data. The complete file has many more of these blocks and each block have more 'properties' than what I'm showing here (Keeping it small and readable :-) ). Note that the third block as a icon property that the other items don't have.

$Data = @'
    c_first_item = {    
        time = 270  
        category = cat_red
        min_value = {
            int = 10
        }
        max_value = {
            int = 20
        }
    }

    c_second_item = {   
        time = 270
        category = cat_blue
        min_value = {
            float = 10
        }
        max_value = {
            float = 20
        }
    }

    c_third_item = {    
        time = 270
        icon = c_third_icon
        category = cat_red
        min_value = {
            int = 10
        }
        max_value = {
            int = 20
        }
    }
'@

And here's my template used by ConvertFrom-String.

    $Template = @'
    {Name*:c_first_item} = \{   
        time = 270

        category = {Category:cat_red}


        random_property = \{
            random_property_value = 10
        \}

        random_property = \{
            random_property_value = 20
        \}
    \}

    {Name*:c_second_item} = \{
        time = 100
        icon = {Picture?:c_third_icon}

        category = {Category:cat_blue}

        random_property = \{
            random_property_value = 10
        \}

        random_property = \{
            random_property_value = 20
        \}
    \}
'@  

I was running this code with ConvertFrom-String to parse the data I wanted :

$Result = $Data | ConvertFrom-String -TemplateContent $Template

The expected result should have looked like this :

Name          Category  Picture
----          --------  -------
c_first_item  cat_red   
c_second_item cat_blue
c_third_item  cat_red   c_third_icon

But I was getting this instead :

Name          Category
----          --------
c_first_item  cat_red   
c_second_item cat_blue
c_third_item  cat_red  

As you can see there's no Picture property in my output object as ConvertFrom-String was unable to parse the data accordingly to what I asked. I guess the examples I'm giving aren't good enough to teach the algorythm how to react.

Is there a way for me to teach ConvertFrom-String to get an empty Picture property for the blocks where icon doesn't exists?


Update: 25th of January

As requested by @iRon, here's an example that adds a Length property to the result.

First, the file I'm parsing (Test.txt) :

d_mem_towers_sulfuric_forest = {
    is_for_colonizeable = yes
    category = deposit_cat_rare

    icon = d_radioactive_wasteland

    resources = {
        category = planet_deposits
        produces = {
            society_research = 4
            energy = 4
        }
    }

    planet_modifier = {
        planet_jobs_energy_produces_mult = 0.10
        planet_jobs_society_research_produces_mult = 0.10
        planet_max_districts_add = -1
        pop_environment_tolerance = -0.15
    }

    drop_weight = {
        weight = 0
    }
}

Then, the template file (Template.txt) :

{Object*:{Name:d_mem_sadrell_capital} = \{
    is_for_colonizeable = yes
    category = {Category:deposit_cat_rare}

    icon = {Picture:d_building}

    planet_modifier = \{
        planet_housing_add = 3
        pop_environment_tolerance = 0.1
        planet_jobs_specialist_produces_mult = 0.15
    \}

    triggered_planet_modifier = \{
        potential = \{
            exists = owner
            owner = \{ is_regular_empire = yes \}
        \}
        modifier = \{
            job_researcher_add = 2
            job_clerk_add = 2
        \}
    \}

    triggered_planet_modifier = \{
        potential = \{
            exists = owner
            owner = \{ is_hive_empire = yes \}
        \}
        modifier = \{
            job_brain_drone_add = 2
            job_maintenance_drone_add = 2
        \}
    \}

    triggered_planet_modifier = \{
        potential = \{
            exists = owner
            owner = \{ is_machine_empire = yes \}
        \}
        modifier = \{   
            job_calculator_add = 2
            job_maintenance_drone_add = 2
        \}
    \}

    drop_weight = \{
        weight = 0
    \}
\}}

{Object*:{Name:d_mem_sadrell_industrial_complex} = \{
    is_for_colonizeable = yes
    category = {Category:deposit_cat_rare}

    icon = {Picture:d_crater}

    planet_modifier = \{
        pop_environment_tolerance = -0.1
        planet_jobs_minerals_produces_mult = 0.25
        district_mining_max = 4
    \}

    drop_weight = \{
        weight = 0
    \}
\}}

{Object*:{Name:_mem_strange_mountain} = \{
    time = 365
    is_for_colonizeable = yes
    category = {Category:deposit_cat_blockers}

    icon = {Picture:d_mem_strange_mountain}

    resources = \{
        category = deposit_blockers
        cost = \{
            energy = 1000
            minerals = 1000
        \}
    \}

    drop_weight = \{
        weight = 0
    \}
\}}

Finally, here's the piece of code I'm using to extract the data :

$testText = Get-Content  -Path "Path\to\text.txt" #MEM
$template = 'Path\to\template.txt'

$result = $testText |
    ConvertFrom-String -TemplateFile $template |
    Select-Object -ExpandProperty Object |
    Union-Object

$result

The expected result should be like that :

Name                         Category         Picture               
----                         --------         -------        
d_mem_towers_sulfuric_forest deposit_cat_rare d_radioactive_wasteland

But I'm getting this instead :

Name                         Category         Picture                 Length
----                         --------         -------                 ------
d_mem_towers_sulfuric_forest deposit_cat_rare d_radioactive_wasteland
                                                                      0

There's a second line to my object that contains a Length property that I don't parse so I guess it somehow comes from Union-Object.

Any idea?

2 Answers

Although all your properties (including Picture) are in the $Result object, they will not be output (to the display) as not all (especially the first) objects contain the Picture property (see related the issue: Not all properties displayed):

PS C:\> $Result

Name          Category
----          --------
c_first_item  cat_red
c_second_item cat_blue
c_third_item  cat_red

The Union-Object cmdlet will add all the missing properties (with a $Null value) to the concerned objects:

PS C:\> $Result = $Result | Union-Object
PS C:\> $Result

Name          Category Picture
----          -------- -------
c_first_item  cat_red
c_second_item cat_blue
c_third_item  cat_red
                       cat_blue

After a slight delay, here's the answer. I was helped by Nicolas Baudin (https://www.linkedin.com/in/nicolas-baudin-63491a111/) to find it.

The problem came from the template. As the icon property is not existent in all sets, the function had difficulties to retrieve it and it was in fact not returning it at all. The trick has been to change the template to include a structure that would then be returned with all the properties, including Picture. This structure is returned by ConvertFrom-String as an object whose properties are all the value you try to parse.

The new template looks like this :

$Template = @'
{Object:*{Name:c_first_item} = \{   
    time = 270

    category = {Category:cat_red}


    random_property = \{
        random_property_value = 10
    \}

    random_property = \{
        random_property_value = 20
    \}
\}}

{Object:*{Name:c_second_item} = \{
    time = 100
    icon = {Picture?:c_third_icon}

    category = {Category:cat_blue}

    random_property = \{
        random_property_value = 10
    \}

    random_property = \{
        random_property_value = 20
    \}
\}}
'@  

The command itself has been adapted to match the new outputted object and it is now :

$Result = $Data | ConvertFrom-String -TemplateContent $Template |
                  Select-Object -ExpandProperty Object |
                  Union-Object

As you can see, I'm using your Union-Object function which come handy as I have several templates that don't output the same property for each object. This way, I can have a single function to parse all my files.

Related