How can you use GetType().GetProperties() on a PowerShell Custom PSObject?

7.3k views Asked by At

I am trying to get this log4net customization to work with PowerShell PSObjects. Basically, this pattern layout will iterate over the object and dump its properties into CSV columns. Here is the code that does the reflection:

PropertyInfo[] properties = loggingEvent.MessageObject.GetType().GetProperties();
            foreach (PropertyInfo prop in properties)
            {
                object value = prop.GetValue(loggingEvent.MessageObject, null);
                loggingEvent.Properties[prop.Name] = value;
            }

This works fine when using a normal class in C#. It also works if I create a class using inline C# in PowerShell like this.

$code = @”

using System;
namespace CsvTest { 
    public class MyEvent
        {
            public string UserId { get; set; }
            public string Details { get; set; }
            public int EventCode { get; set; }

        }
    }
“@

Add-Type $code

$test = New-Object CsvTest.MyEvent
$test.UserId = "Andy"
$test.Details ="SomeStuff"
$test.EventCode = 10

$MyCommonLogger.Error($test)
$MyCommonLogger.Info($test)
$MyCommonLogger.Warn($test)

What doesn't work is if I create a custom PSObject. If I replace the $code with a PSCustom object such as

$obj = New-Object psobject -Property @{
                        "UserId" = "Andy";
                        "EventCode" = 100;
                        "Details" = "DetailInfoGoeshere"
                        }

Then it will just return Null. I have verified that this does in fact return null.

$obj = New-Object psobject -Property @{
                        "UserId" = "Andy";
                        "EventCode" = 100;
                        "Details" = "DetailInfoGoeshere"
                        }

$obj.GetType().GetProperties()

I suppose I could hack up CSVLogging Pattern layout and make a special case if the inbound object is a PSObject, but I'd rather not have to customize it for just one type of object. Is there a way in PowerShell to take a psobject and covert/cast it into something that I can use with GetType().GetProperties()?

2

There are 2 answers

0
Andy Schneider On

I ended up writing the code in PowerShell to generate the class I needed. I added the "Test-IsAssemblyLoaded" function so that it wouldn't attempt to load the same assembly twice in the same PS runsapce. Kind of dirty but works.

Function Test-IsAssemblyLoaded {
param ($assembly)

$result = $false
try {
        $expression = "[$assembly]| out-null"
        invoke-expression $expression;
        $result = $true
    } 


catch {$result = $false}

return $result
}


Function Add-DotNetClass {
param(

[Parameter()]
$NameSpace = "PowerShell",

[Parameter()]
$Properties,

[Parameter()]
$ClassName

)

$code = @"

using System;

namespace $Namespace
{
    public class $className 
    {
        $(foreach ($p in $Properties)
                {
    "public $p {get;set;} `n"
                } )

    }
}
"@

if (Test-IsAssemblyLoaded "$Namespace.$ClassName") {Write-Warning "Assembly already loaded"}
else {Add-Type $code}

}

It seems to do the trick.

0
JPBlanc On

As far as I understand, according to the result of :

Get-Member -InputObject $obj -Force

The ways to get psObject properties are :

$obj.psobject.properties
$obj.psobject.get_properties()

Can't you write the C# code that dynamicaly create the class ?