O que é a propriedade ParameterizedProperty Chars no tipo de string adicionado pelo Powershell?

Oct 04 2020

Por favor, observe:

C:\> ''|Get-Member |? { $_.MemberType -eq 'ParameterizedProperty' }


   TypeName: System.String

Name  MemberType            Definition
----  ----------            ----------
Chars ParameterizedProperty char Chars(int index) {get;}


C:\>

Esta é uma propriedade muito estranha. Em primeiro lugar, é adicionado pelo Powershell, a seguir contém uma propriedade recursiva infinita:

C:\> ''.Chars


IsSettable          : False
IsGettable          : True
OverloadDefinitions : {char Chars(int index) {get;}}
TypeNameOfValue     : System.Char
MemberType          : ParameterizedProperty
Value               : char Chars(int index) {get;}
Name                : Chars
IsInstance          : True



C:\> ''.Chars.Value


IsSettable          : False
IsGettable          : True
OverloadDefinitions : {char Chars(int index) {get;}}
TypeNameOfValue     : System.Char
MemberType          : ParameterizedProperty
Value               : char Chars(int index) {get;}
Name                : Chars
IsInstance          : True



C:\> ''.Chars.GetHashCode()
56544304
C:\> ''.Chars.Value.GetHashCode()
34626228
C:\> ''.Chars.Value.Value.GetHashCode()
3756075
C:\> ''.Chars.Value.Value.Value.GetHashCode()
49108342
C:\> ''.Chars.Value.Value.Value.Value.GetHashCode()
62340979
C:\> ''.Chars.Value.Value.Value.Value.Value.GetHashCode()
24678148
C:\>

O código hash é diferente a cada vez, portanto, deve ser gerado dinamicamente.

Por que eu me importo? Estou tentando usar um módulo Newtonsoft.Json PowerShell de PSGallery e ele bloqueia com essa propriedade, mas apenas quando executado no Desktop PowerShell (5.1), não no Core (7.0.3). O problema é que não tenho uma reprodução mínima, o objeto de entrada é bastante grande. O erro que recebo é:

ConvertTo-JsonNewtonsoft : Exception calling "SerializeObject" with "2" argument(s): "Self referencing loop detected for property 'Value' with type 'System.Management.Automation.PSParameterizedProperty'. Path 'environments[4].conditions.name.Chars'."

Esse problema não existe no PS Core.

Alguém pode me explicar o que é essa propriedade, por que precisamos dela e como podemos nos livrar dela?

EDITAR 1

Acho que é um problema com o Newtonsoft.Jsonmódulo. Observar:

[DBG]> [pscustomobject]@{ a = 1} | ConvertTo-Json
{
  "a": 1
}
 [DBG]>  [pscustomobject]@{ a = 1} | ConvertTo-JsonNewtonsoft
{
  "CliXml": "<Objs Version=\"1.1.0.1\" xmlns=\"http://schemas.microsoft.com/powershell/2004/04\">\r\n  <Obj RefId=\"0\">\r\n    <TN RefId=\"0\">\r\n
 <T>System.Management.Automation.PSCustomObject</T>\r\n      <T>System.Object</T>\r\n    </TN>\r\n    <ToString>@{a=1}</ToString>\r\n    <Obj RefId=\"1\">\r\n      <TNRef RefId=\"0\" />\r\n      <MS>\r\n        <I32 N=\"a\">1</I32>\r\n      </MS>\r\n    </Obj>\r\n    <MS>\r\n      <I32 N=\"a\">1</I32>\r\n    </MS>\r\n  </Obj>\r\n</Objs>"
}
 [DBG]>

Ele é incapaz de interpretar objetos PowerShell adequadamente. Torna inutilizável.

Respostas

mklement0 Oct 06 2020 at 00:43

tl; dr

Seu verdadeiro problema é que nem a Newtonsoft.Jsonbiblioteca nem o módulo de wrapper do PowerShell suportam [pscustomobject]instâncias :

A biblioteca pede que as [pscustomobject]instâncias se serializem , com base em [pscustomobject]( [psobject]) que implementa a ISerializableinterface.

  • No Windows PowerShell, isso falha completamente, provavelmente devido ao fato de a versão agrupada do Newtonsoft.Json.dllassembly ser muito antiga (no momento em que este texto foi escrito, a versão agrupada é 8.0, embora 12.0seja a atual) e ter um bug

    • A manifestação desse bug é o Self referencing loop detected for property 'Value' ...bug que você viu.
  • No PowerShell [Core] v6 +, a versão mais recente Newtonsoft.Json.dllque vem com o PowerShell se antecipa à versão obsoleta, portanto, o erro não ocorre, mas o problema de serialização se torna óbvio:

    • O { "CliXml": "<Objs Version=\"1.1.0.1\" .. }texto JSON resultante mostra que a [pscustomobject]instância foi serializada no formato CLIXML, o formato de serialização baseado em XML nativo do PowerShell, usado principalmente pelo recurso remoto do PowerShell.

    • É hipoteticamente possível - embora muito complicado - desserializar manualmente tal JSON, pós-processando-o e substituindo objetos com apenas uma CliXmlpropriedade com o valor de retorno de[System.Management.Automation.PSSerializer]::Deserialize()


Soluções :

  • Se sua intenção é simplesmente comparar representações serializadas em uma forma agnóstica de edição PS, independentemente do formato de serialização específico , considere o uso de CLIXML diretamente, via Export-CliXmle Import-CliXml.

  • Se você deseja uma forma agnóstica de edição PS para serializar para JSON , você terá que lançar seu próprio [pscustomobject]conversor de hashtable -to-encomendado, porque serializar hashtables ordenados ( [ordered] @{ ... }, System.Collections.Specialized.OrderedDictionary) via Newtonsoft.Json faz o round-trip corretamente no PowerShell (é, na verdade, a estrutura de dados usada pelos ConvertFrom/To-JsonNewtonsoftcmdlets do wrapper).

Ambas as abordagens são demonstradas nesta resposta relacionada .