4

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?

Tuttu
  • 137
  • 9
  • Try: `$Result |` [`Union-Object`](https://stackoverflow.com/a/44429084/1701026) – iRon Jan 10 '19 at 21:10
  • @iRon : This function couldn't help me get a result that the parsing don't return first. Or there's something I don't understand. :) – Tuttu Jan 11 '19 at 07:18
  • To troubleshoot the issue from **Update: 25th of January**: remove the `| Union-Object` from the `$Result`. Instead convert it to JSON (`$Result | ConvertTo-Json`) or to a PowerShell expression (`$Result |` [`ConvertTo-Expression`](https://www.powershellgallery.com/packages/ConvertTo-Expression)) and notice the empty string at the end of the stream. This is the cause of the length property in the result similar to: `"" | Union-Object` (See also: `"" | Get-Member -Type Property`). – iRon Jan 25 '19 at 19:35
  • You can also see the different output types with: `$result | %{$_.GetType()}`. Anyways, the solution is to only include objects that contain something (`... | Where-Object {$_} | Union-Objec`) or just exclude the strings from the object list (`... | Where-Object {$_ -isNot [String]} | Union-Object`). – iRon Jan 26 '19 at 08:24
  • Thanks for the taking the time @iRon. It looks like the `SelectFrom-String` somehow generates unwanted data. I will simply filter them as you suggest so they don't bother me anymore. :) – Tuttu Jan 26 '19 at 10:24

2 Answers2

1

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
iRon
  • 20,463
  • 10
  • 53
  • 79
  • Thanks for your explanation (And for the link to the help center) and sorry about the poor question thread. I will update it with clearer information and let you know when it's done. Also, I might have found the answer with the help of someone. I need to try it first. :) – Tuttu Jan 11 '19 at 18:56
  • Thanks for updating your question, I have incorporated it in my answer accordingly – iRon Jan 12 '19 at 18:19
  • Thanks for your updating answer. Unfortunately, Union-Object will still not work here as with this template, `ConvertFrom-String` will never return the `Picture` property. Someone on Slack helped me figure a working answer so I will give him a few days to post it here himself or I will do it myself and credit him for his help. :) – Tuttu Jan 14 '19 at 10:43
  • Thought your `Union-Object` couldn't solve my problem, it's been pretty useful to get a simpler script. I'm not sure if I can accept your answer as per SO standard however. If a mod could comment on that. :) – Tuttu Jan 17 '19 at 17:24
  • I might have found an edge case where you `Union-Object` returns a `Length` property that shouldn't exists. Where could be talk about that? – Tuttu Jan 17 '19 at 19:49
  • Still there @iRon ? – Tuttu Jan 23 '19 at 09:28
  • @Tuttu, sorry, I missed your previous comment. I guess the issue is because your input contains just a single string (which is an object by itself), e.g. `"Test" | Union-Object`, just provides a `Length` of `4`. If this is not the case, can you provide a some sample `data` and/or `template` in a comment? Or Add it to the Question? (if not let me know, I will put the `Union-Object` cmdlet on the GitHub, where you can place a bug report.) – iRon Jan 23 '19 at 16:01
  • No problem @iRon. For some reason, the mention got lost in my first comment. I will try to add some code to my question and let you know when it's done. Thanks for the help. :) – Tuttu Jan 23 '19 at 20:20
  • Sorry for the delay @iRon. I was unable to reproduce the problem at first but I have now updated my question with code that will help you reproduce it. :) – Tuttu Jan 25 '19 at 09:26
1

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.

Tuttu
  • 137
  • 9