Qual è la proprietà ParameterizedProperty Chars sul tipo di stringa aggiunto da Powershell?

Oct 04 2020

Si prega di osservare:

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


   TypeName: System.String

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


C:\>

Questa è una proprietà molto strana. Prima di tutto viene aggiunto da Powershell, successivamente contiene una proprietà ricorsiva 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:\>

Il codice hash è ogni volta diverso, quindi deve essere generato dinamicamente.

Perché mi interessa? Sto cercando di utilizzare un modulo Newtonsoft.Json PowerShell da PSGallery e si blocca su questa proprietà, ma solo quando viene eseguito in Desktop PowerShell (5.1), non in Core (7.0.3). Il problema è che non ho una riproduzione minima, l'oggetto di input è abbastanza grande. L'errore che ottengo è:

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'."

Nessun problema di questo tipo esiste in PS Core.

Qualcuno può spiegarmi cos'è questa proprietà, perché ne abbiamo bisogno e come possiamo sbarazzarcene?

MODIFICA 1

Immagino sia un problema con il Newtonsoft.Jsonmodulo. Osservare:

[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]>

Non è in grado di interpretare correttamente gli oggetti PowerShell. Lo rende inutilizzabile.

Risposte

mklement0 Oct 06 2020 at 00:43

tl; dr

Il tuo vero problema è che né la Newtonsoft.Jsonlibreria né il modulo wrapper di PowerShell supportano le [pscustomobject]istanze :

La libreria chiede [pscustomobject]alle istanze di serializzarsi da sole , in base a [pscustomobject]( [psobject]) che implementa l' ISerializableinterfaccia.

  • In Windows PowerShell questo accade per fallire del tutto, presumibilmente a causa del fatto che la versione in bundle Newtonsoft.Json.dlldell'assembly è piuttosto vecchia (al momento della stesura di questo documento, la versione in bundle è 8.0, mentre 12.0è attuale) e ha un bug

    • La manifestazione di questo bug è il Self referencing loop detected for property 'Value' ...bug che hai visto.
  • In PowerShell [Core] v6 +, la versione più recente di Newtonsoft.Json.dllquella fornita con PowerShell stesso precede la versione obsoleta, quindi l'errore non si verifica, ma il problema di serializzazione diventa ovvio:

    • Il { "CliXml": "<Objs Version=\"1.1.0.1\" .. }testo JSON risultante mostra che l' [pscustomobject]istanza è stata serializzata in formato CLIXML, il formato di serializzazione nativo basato su XML di PowerShell, utilizzato in particolare dalla funzionalità di comunicazione remota di PowerShell.

    • È ipoteticamente possibile, sebbene molto complicato, deserializzare manualmente tale JSON post-elaborandolo e sostituendo gli oggetti con solo una CliXmlproprietà con il valore restituito da[System.Management.Automation.PSSerializer]::Deserialize()


Soluzioni :

  • Se il tuo intento è semplicemente quello di confrontare le rappresentazioni serializzate in una forma indipendente dall'edizione PS, indipendentemente dal formato di serializzazione specifico , considera l'utilizzo di CLIXML direttamente, tramite Export-CliXmle Import-CliXml.

  • Se desideri un modo indipendente dall'edizione PS per serializzare in JSON , dovrai eseguire il roll [pscustomobject]-up del tuo convertitore-to-order-hashtable, perché la serializzazione degli hashtable ordinati ( [ordered] @{ ... }, System.Collections.Specialized.OrderedDictionary) tramite Newtonsoft.Json esegue correttamente il round trip in PowerShell (è, infatti, la struttura dati utilizzata dai ConvertFrom/To-JsonNewtonsoftcmdlet wrapper).

Entrambi gli approcci sono dimostrati in questa risposta correlata .