ConsoleGuiTools icon indicating copy to clipboard operation
ConsoleGuiTools copied to clipboard

Fix #242 - Adds support for PSCustomObject

Open tznind opened this issue 1 year ago • 8 comments

PR Summary

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

  • Extracts nested 'cached member' classes to seperate files
  • Adds interface and abstract base
  • Adds implementation for PSObject properties

Fixes #242

Test JSON
$Values = @'
{
	"Candidate":
	{
		"Name": {
			"3rd": "Surname",
			"1st": "Forename",
			"2nd": "Middle name"
		},
		"Date of Birth": "+1582-10-15",
		"E-mail Address": "mailTo:[email protected]",
		"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:[email protected]",
		"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:[email protected]",
		"Mobile Telephone Number": "tel:+00-0000-000000",
	},
}
'@
Test JSON 2

image 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 avatar Mar 17 '24 10:03 tznind

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

tznind avatar Mar 24 '24 07:03 tznind

@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?

tig avatar Mar 25 '24 16:03 tig

@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 avatar Mar 25 '24 18:03 tznind

@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

tznind avatar Mar 30 '24 10:03 tznind

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…

ThomasNieto avatar Apr 01 '24 15:04 ThomasNieto

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

tznind avatar Apr 03 '24 03:04 tznind

Any plans on merging this?

comnam90 avatar Jun 05 '25 02:06 comnam90

Bumping this, are there any plans to merge this in?

joshbradfield-kiwirail avatar Oct 15 '25 03:10 joshbradfield-kiwirail