PowerShell / ConsoleGuiTools

Modules that mix PowerShell and GUIs/CUIs!
https://www.powershellgallery.com/packages/Microsoft.PowerShell.ConsoleGuiTools
MIT License
790 stars 60 forks source link

Fix #242 - Adds support for PSCustomObject #243

Open tznind opened 6 months ago

tznind commented 6 months ago

PR Summary

Adds support for dealing with native PSObject instances (current code only deals with native object types).

Fixes #242

Test JSON ``` $Values = @' { "Candidate": { "Name": { "3rd": "Surname", "1st": "Forename", "2nd": "Middle name" }, "Date of Birth": "+1582-10-15", "E-mail Address": "mailTo:address@domain.TLD", "Mobile Telephone Number": "tel:+00-0000-000000", "Blood Group": "" // Unknown }, "Mother": { "Name": { "3rd": "Surname", "1st": "Forename", "2nd": "Middle name" }, "Date of Birth": "+1582-10-15", "E-mail Address": "mailTo:address@domain.TLD", "Mobile Telephone Number": "tel:+00-0000-000000", }, "Father": { "Name": { "3rd": "Surname", "1st": "Forename", "2nd": "Middle name" }, "Date of Birth": "+1582-10-15", "E-mail Address": "mailTo:address@domain.TLD", "Mobile Telephone Number": "tel:+00-0000-000000", }, } '@ ```
Test JSON 2 ![image](https://github.com/PowerShell/GraphicalTools/assets/31306100/d487d985-bf2c-409d-b76e-0892e8b84f35) _Test JSON 2 loaded in Show-ObjectTree_ ``` $Values = @' { "name": "Root", "value": "Root Value", "children": [ { "name": "Child 1", "value": "Child 1 Value", "children": [ { "name": "Grandchild 1", "value": "Grandchild 1 Value", "children": [ { "name": "Great-Grandchild 1", "value": "Great-Grandchild 1 Value", "attributes": { "attribute1": "Value1", "attribute2": "Value2" }, "relatedObjects": [ { "id": 123, "name": "Related Object 1" }, { "id": 124, "name": "Related Object 2" } ] }, { "name": "Great-Grandchild 2", "value": "Great-Grandchild 2 Value", "attributes": { "attribute1": "Value1", "attribute2": "Value2" }, "relatedObjects": [ { "id": 125, "name": "Related Object 3" } ] } ] }, { "name": "Grandchild 2", "value": "Grandchild 2 Value", "attributes": { "attribute1": "Value1", "attribute2": "Value2" } } ] }, { "name": "Child 2", "value": "Child 2 Value", "children": [ { "name": "Grandchild 3", "value": "Grandchild 3 Value", "attributes": { "attribute1": "Value1", "attribute2": "Value2" } }, { "name": "Grandchild 4", "value": "Grandchild 4 Value", "attributes": { "attribute1": "Value1", "attribute2": "Value2" } } ], "arrayProperty": [1, 2, 3, 4] }, { "name": "Child 3", "value": "Child 3 Value", "attributes": { "attribute1": "Value1", "attribute2": "Value2" }, "arrayProperty": ["a", "b", "c"] } ] } '@ ```
How to manually test with the above JSON ``` Invoke-Build Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools pwsh import-Module ./module/Microsoft.PowerShell.ConsoleGuiTools $Values = @' { ... } '@ $Values | ConvertFrom-JSON | Show-ObjectTree ``` _A new pwsh window is launched to avoid conflict of modules. Exit after test with `exit`._

jsontree

PR Context

tznind commented 6 months ago

@andyleejordan this is ready for review if you are able to find time 🚀

tig commented 6 months ago

@tznind We need to fix this in ocgv too. I haven't had a chance to look at your fix here closely, so I'm curious if you spent anytime on that?

tznind commented 6 months ago

@tznind We need to fix this in ocgv too. I haven't had a chance to look at your fix here closely, so I'm curious if you spent anytime on that?

Ah, no I did not look at the ocgv command. I did create a helper though shouldn't be too hard to port across. I can probably take a look at the weekend.

tznind commented 6 months ago

@tig I have looked into the ocgv implementation and do not think it will have the same issues that the tree view had (that this PR corrects).

ocgv uses GetFormatViewDefinitionForObject and CastObjectsToTableView to deal with the input PSObject directly while tree view involves itself quite heavily in 'Root object' being wrapped by the PSObject (e.g. to handle cases like directories etc).

Both of the below look like correct behaviours to me, see example json

$Values = @'
[
  {
    "id": 1,
    "name": "John",
    "age": 30,
    "city": "New York",
    "people":
    [
        {
            "name": "Alice"
        },
        {
            "name": "Frank"
        }
    ]
  },
  {
    "id": 2,
    "name": "Alice",
    "age": 25,
    "city": "Los Angeles"
  },
  {
    "id": 3,
    "name": "Bob",
    "age": 35,
    "city": "Chicago"
  }
]
'@

You get image

With tree view you get image

ThomasNieto commented 6 months ago

Does this enhancement also handle the scenario of real .NET objects like FileInfo with PowerShell's ETS (Extended Type System) properties?

Here is an example with Get-ChildItem with FileInfo and DirectoryInfo.

PS C:\Users\thomas> Get-ChildItem | gm | where MemberType -notin property,method

   TypeName: System.IO.DirectoryInfo

Name                MemberType     Definition
----                ----------     ----------
Target              AliasProperty  Target = LinkTarget
LinkType            CodeProperty   System.String LinkType{get=GetLinkType;}
Mode                CodeProperty   System.String Mode{get=Mode;}
ModeWithoutHardLink CodeProperty   System.String ModeWithoutHardLink{get=ModeWithoutHardLink;}
ResolvedTarget      CodeProperty   System.String ResolvedTarget{get=ResolvedTarget;}
PSChildName         NoteProperty   string PSChildName=.oci
PSDrive             NoteProperty   PSDriveInfo PSDrive=C
PSIsContainer       NoteProperty   bool PSIsContainer=True
PSParentPath        NoteProperty   string PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\thomas
PSPath              NoteProperty   string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\thomas\.oci
PSProvider          NoteProperty   ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
BaseName            ScriptProperty System.Object BaseName {get=$this.Name;}

   TypeName: System.IO.FileInfo

Name                MemberType     Definition
----                ----------     ----------
Target              AliasProperty  Target = LinkTarget
LinkType            CodeProperty   System.String LinkType{get=GetLinkType;}
Mode                CodeProperty   System.String Mode{get=Mode;}
ModeWithoutHardLink CodeProperty   System.String ModeWithoutHardLink{get=ModeWithoutHardLink;}
ResolvedTarget      CodeProperty   System.String ResolvedTarget{get=ResolvedTarget;}
PSChildName         NoteProperty   string PSChildName=_viminfo
PSDrive             NoteProperty   PSDriveInfo PSDrive=C
PSIsContainer       NoteProperty   bool PSIsContainer=False
PSParentPath        NoteProperty   string PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\thomas
PSPath              NoteProperty   string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\thomas\_viminfo
PSProvider          NoteProperty   ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
BaseName            ScriptProperty System.Object BaseName {get=if ($this.Extension.Length -gt 0){$this.Name.Remove($th…
VersionInfo         ScriptProperty System.Object VersionInfo {get=[System.Diagnostics.FileVersionInfo]::GetVersionInfo…
tznind commented 6 months ago

Does this enhancement also handle the scenario of real .NET objects like FileInfo with PowerShell's ETS (Extended Type System) properties?

Here is an example with Get-ChildItem with FileInfo and DirectoryInfo.

[...]

I am not sure I fully understand the question. But I think the answer is yes.

The default operating proceedure for the tree view is to look for a real .NET object and enumerate its properties as leaves/branches. For example PS D:\Repos\GraphicalTools> Get-ChildItem | shot

image piping get children to tree view

Note that in the case of DirectoryInfo the sub folders are also listed after properties.

However calling gm would flatten the tree which would not be helpful, so calling Get-ChildItem | gm | where MemberType -notin property,method | shot just lists the member definitions:

image piping gm output is not particularly helpful