Home > Blockchain >  Format-List -force * not showing all members for .net object even though Get-Member does show it
Format-List -force * not showing all members for .net object even though Get-Member does show it

Time:03-16

Hope someone can shine a light on this, really annoying.

I'm trying to find all the links in a MarkdownDocument using Markdig in Powershell 7.1.3

It's designed for .NET and it has a set of inherited classes that it uses to represent various types of markdown documents. I'm using a simple helper PS wrapper that helps using a generic method, but I don't think this is a cause.

Let me show you with a simple repro

install-module psmarkdig # provides the Get-MdElement helper
$doc = [Markdig.Parsers.MarkdownParser]::Parse('# blah`n`nHello [this is a link](linkpath.md)')
$links = @(Get-MdElement $doc 'Markdig.Syntax.Inlines.LinkInline') # force even a single match to be an array

OK, so this contains a single link

> $links.Count
1

and the element is the type we expected

> $links[0].GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    LinkInline                               Markdig.Syntax.Inlines.ContainerInline

I can see that there is an IsImage property with Get-Member

> Get-Member -InputObject $links[0]
   TypeName: Markdig.Syntax.Inlines.LinkInline

Name                 MemberType Definition
----                 ---------- ----------
AppendChild          Method     Markdig.Syntax.Inlines.ContainerInline AppendChild(Markdig.Syntax.Inlines.Inline child)
Clear                Method     void Clear()
ContainsChild        Method     bool ContainsChild(Markdig.Syntax.Inlines.Inline childToFind)
ContainsData         Method     bool ContainsData(System.Object key), bool IMarkdownObject.ContainsData(System.Object key)
ContainsParentOfType Method     bool ContainsParentOfType[T]()
DumpTo               Method     void DumpTo(System.IO.TextWriter writer), void DumpTo(System.IO.TextWriter writer, int level)
EmbraceChildrenBy    Method     void EmbraceChildrenBy(Markdig.Syntax.Inlines.ContainerInline container)
Equals               Method     bool Equals(System.Object obj)
FindBestParent       Method     Markdig.Syntax.Inlines.Inline FindBestParent()
FindDescendants      Method     System.Collections.Generic.IEnumerable[T] FindDescendants[T]()
FindParentOfType     Method     System.Collections.Generic.IEnumerable[T] FindParentOfType[T]()
GetData              Method     System.Object GetData(System.Object key), System.Object IMarkdownObject.GetData(System.Object key)
GetEnumerator        Method     Markdig.Syntax.Inlines.ContainerInline Enumerator GetEnumerator(), System.Collections.Generic.IEnumerator[Markdig.Syn…
GetHashCode          Method     int GetHashCode()
GetType              Method     type GetType()
InsertAfter          Method     void InsertAfter(Markdig.Syntax.Inlines.Inline next)
InsertBefore         Method     void InsertBefore(Markdig.Syntax.Inlines.Inline previous)
MoveChildrenAfter    Method     void MoveChildrenAfter(Markdig.Syntax.Inlines.Inline parent)
Remove               Method     void Remove()
RemoveData           Method     bool RemoveData(System.Object key), bool IMarkdownObject.RemoveData(System.Object key)
ReplaceBy            Method     Markdig.Syntax.Inlines.Inline ReplaceBy(Markdig.Syntax.Inlines.Inline inline, bool copyChildren)
SetData              Method     void SetData(System.Object key, System.Object value), void IMarkdownObject.SetData(System.Object key, System.Object v…
ToPositionText       Method     string ToPositionText()
ToString             Method     string ToString()
Column               Property   int Column {get;set;}
FirstChild           Property   Markdig.Syntax.Inlines.Inline FirstChild {get;}
GetDynamicUrl        Property   Markdig.Syntax.Inlines.LinkInline GetUrlDelegate GetDynamicUrl {get;set;}
IsAutoLink           Property   bool IsAutoLink {get;set;}
IsClosed             Property   bool IsClosed {get;set;}
IsImage              Property   bool IsImage {get;set;}
IsShortcut           Property   bool IsShortcut {get;set;}
Label                Property   string Label {get;set;}
LabelSpan            Property   System.Nullable[Markdig.Syntax.SourceSpan] LabelSpan {get;set;}
LastChild            Property   Markdig.Syntax.Inlines.Inline LastChild {get;}
Line                 Property   int Line {get;set;}
NextSibling          Property   Markdig.Syntax.Inlines.Inline NextSibling {get;}
Parent               Property   Markdig.Syntax.Inlines.ContainerInline Parent {get;}
PreviousSibling      Property   Markdig.Syntax.Inlines.Inline PreviousSibling {get;}
Reference            Property   Markdig.Syntax.LinkReferenceDefinition Reference {get;set;}
Span                 Property   Markdig.Syntax.SourceSpan Span {get;set;}
Title                Property   string Title {get;set;}
TitleSpan            Property   System.Nullable[Markdig.Syntax.SourceSpan] TitleSpan {get;set;}
Url                  Property   string Url {get;set;}
UrlSpan              Property   System.Nullable[Markdig.Syntax.SourceSpan] UrlSpan {get;set;}

However Format-List gives me a very limited view

> Format-List -InputObject $links[0]
IsFirstCharacterEscaped : False
Parent                  : {this is a link}
PreviousSibling         :
NextSibling             :
IsClosed                : False
Column                  : 0
Line                    : 0
Content                 : this is a link
Span                    : 0-0

Ok, so perhaps there is some FormattingTypeViews going on, but even throwing -force -property * and -TheKitchenSink doesn't seem to make any difference!

> Format-List -InputObject $links[0] -Property * -Force
IsFirstCharacterEscaped : False
Parent                  : {this is a link}
PreviousSibling         :
NextSibling             :
IsClosed                : False
Column                  : 0
Line                    : 0
Content                 : this is a link
Span                    : 0-0

I can see the value of IsImage if I call it

> $links[0].IsImage
False

How can I explore these objects in the normal way that I'm used to doing in Powershell using Format-List? Is it a Powershell bug that I should raise somehow?

CodePudding user response:

Markdig.Syntax.Inlines.LinkInline itself implements IEnumerable, which cause Format-List to enumerate it and report the properties of the enumerated elements, even when passed via -InputObject.

Specifically, it enumerates the instance's one child element, which is of type Markdig.Syntax.Inlines.LiteralInline, and its properties are being displayed.

To prevent this enumeration, i.e. to see the properties of the instance itself, you must wrap it in an aux. single-element array:

Format-List -InputObject (, $links[0])

Output:

Url             : linkpath.md
GetDynamicUrl   : 
Title           : 
Label           : 
IsImage         : False
IsShortcut      : False
IsAutoLink      : False
Reference       : 
ParentBlock     : 
FirstChild      : this is a link
LastChild       : this is a link
Parent          : {blah, Markdig.Syntax.Inlines.CodeInline, nHello , this is a link}
PreviousSibling : nHello 
NextSibling     : 
IsClosed        : True
Column          : 0
Line            : 0
UrlSpan         : 0-0
TitleSpan       : 0--1
LabelSpan       : 0-0
Span            : 0-0

A simpler way to get just the property names is via the intrinsic .psobject property:

$links[0].psobject.Properties.Name

Note:

  • The problem boils down to Markdig.Syntax.Inlines.LinkInline not being thought of as a collection-like type, yet its implementation of the IEnumerable interface makes PowerShell treat it like a collection.

  • For .NET types to be PowerShell-friendly, they need to expose enumerations via a property that implements IEnumerable, say .Children in this case, rather than implementing IEnumerable themselves.

  • Related