请注意:
C:\> ''|Get-Member |? { $_.MemberType -eq 'ParameterizedProperty' }
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Chars ParameterizedProperty char Chars(int index) {get;}
C:\>
Run Code Online (Sandbox Code Playgroud)
这是一个非常奇怪的属性。首先它是由 Powershell 添加的,然后它包含一个无限递归属性:
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:\>
Run Code Online (Sandbox Code Playgroud)
哈希码每次都不一样,所以必须动态生成。
我为什么在乎?我正在尝试使用来自 PSGallery的Newtonsoft.Json PowerShell 模块,它在此属性上阻塞,但仅当在桌面 PowerShell (5.1) 中运行时,而不是在 Core (7.0.3) 中运行时。问题是我没有最小复制,输入对象非常大。我得到的错误是:
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'."
Run Code Online (Sandbox Code Playgroud)
PS Core 中不存在此类问题。
有人可以向我解释这个财产是什么,我们为什么需要它以及我们如何摆脱它?
编辑 1
估计是Newtonsoft.Json模块有问题。观察:
[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]>
Run Code Online (Sandbox Code Playgroud)
它无法正确解释 powershell 对象。使其无法使用。
太长了;博士
您真正的问题是该库和它的 PowerShell 包装器模块都不支持实例Newtonsoft.Json[pscustomobject]:
该库要求[pscustomobject]实例根据实现接口的( )来序列化自身。[pscustomobject][psobject]ISerializable
在 Windows PowerShell 中,这会彻底失败,可能是由于程序集的捆绑版本Newtonsoft.Json.dll相当旧(在撰写本文时,捆绑版本是8.0,而是12.0最新版本)并且存在错误
Self referencing loop detected for property 'Value' ...你看到的bug。在 PowerShell [Core] v6+ 中,Newtonsoft.Json.dllPowerShell 本身附带的较新版本会抢占过时的版本,因此不会发生错误,但序列化问题变得明显:
生成的{ "CliXml": "<Objs Version=\"1.1.0.1\" .. }JSON 文本显示该[pscustomobject]实例以 CLIXML 格式序列化,这是 PowerShell 的本机基于 XML 的序列化格式,尤其是 PowerShell 的远程处理功能所使用的格式。
假设可以通过后处理这种 JSON 并仅用具有返回值的属性替换对象来手动反序列化此类 JSON,尽管非常麻烦CliXml[System.Management.Automation.PSSerializer]::Deserialize()
解决方案:
如果您的目的只是比较与 PS 版本无关的形式的序列化表示,而不考虑特定的序列化格式,请考虑通过Export-CliXml和直接使用 CLIXML Import-CliXml。
如果您确实想要以与 PS 版本无关的方式序列化为JSON,则必须使用自己的[pscustomobject]-to-ordered-hashtable 转换器,因为通过 Newtonsoft.Json序列化有序哈希表 ( [ordered] @{ ... }, )可以在 PowerShell 中正确往返(事实上,它是包装器 cmdlet 使用的数据结构)。System.Collections.Specialized.OrderedDictionaryConvertFrom/To-JsonNewtonsoft
此相关答案中演示了这两种方法。