27

为什么我会得到意想不到的ConvertTo-Json结果,为什么会得到类似System.Collections.Hashtable的值和/或为什么往返 ( $Json | ConvertFrom-Json | ConvertTo-Json) 会失败?

元问题

Stackoverflow 有一个很好的机制来防止重复的问题,但据我所知,没有机制可以防止出现重复原因的问题。以这个问题为例:几乎每周都会有一个新问题以相同的原因出现,但通常很难将其定义为重复问题,因为问题本身只是略有不同。尽管如此,如果这个问题/答案本身最终成为重复(或离题),我不会感到惊讶,但不幸的是,stackoverflow 不可能写一篇文章来防止其他程序员继续写由这个“已知”陷阱引起的问题.

重复

具有相同共同原因的类似问题的几个示例:

不同的

那么,这个“自我回答”的问题与上述重复的问题有什么不同吗?
它在标题中有共同的原因,因此它可能更好地防止由于相同的原因而重复提问。

4

2 回答 2

27

回答

ConvertTo-Json有一个-Depth参数:

指定 JSON 表示中包含多少级别的包含对象。默认值为2
_

例子

要使用 JSON 文件进行完整的往返,您需要增加cmdlet-Depth的:ConvertTo-Json

$Json | ConvertFrom-Json | ConvertTo-Json -Depth 9

TL;博士

可能是因为使用 (.Net) 完整类型名称终止比默认( 2ConvertTo-Json )更深的分支,程序员假设存在错误或 cmdlet 限制并且不阅读帮助或关于。就个人而言,我认为在切断分支的末尾 带有一个简单省略号(三个点:...)的字符串会有更清晰的含义(另见:Github issue: 8381-Depth

为什么?

这个问题也经常在另一个讨论中结束:为什么深度是有限的?

一些对象具有循环引用,这意味着子对象可以引用父对象(或其祖父母之一),如果将其序列化为 JSON,则会导致不定式循环。

以下面的哈希表为例,它具有parent引用对象本身的属性:

$Test = @{Guid = New-Guid}
$Test.Parent = $Test

如果您执行:$Test | ConvertTo-Json默认情况下,它将方便地停在深度级别 2 处:

{
    "Guid":  "a274d017-5188-4d91-b960-023c06159dcc",
    "Parent":  {
                   "Guid":  "a274d017-5188-4d91-b960-023c06159dcc",
                   "Parent":  {
                                  "Guid":  "a274d017-5188-4d91-b960-023c06159dcc",
                                  "Parent":  "System.Collections.Hashtable"
                              }
               }
}

这就是为什么将 自动设置-Depth为大的量不是一个好主意的原因。

于 2018-12-02T19:13:53.703 回答
12

更新PowerShell 7.1在发生截断时引入了警告。虽然这比之前的安静截断要好,但下面建议的解决方案对我来说似乎更可取。


您有用的问题和答案ConvertTo-Json清楚地说明了当前默认行为有多大的痛点。

至于行为的正当性

虽然有意截断不需要其完整深度的输入对象树-Depth可能很有用,但从毫无戒心的用户的角度来看,默认悄悄地截断输出相当于序列化的安静事实上的失败- 直到可能不会被发现的失败之后。-Depth 2

看似随意而安静的截断令大多数用户感到惊讶,并且在每次ConvertTo-Json通话中都必须考虑到它是不必要的负担。

我创建了GitHub 问题 #8393,其中包含更改当前行为的建议,具体如下

  • 忽略-Depth对象[pscustomobject]图(概念上 DTO(数据传输对象,“属性包”)的层次结构,例如从 中返回Convert*From*-Json)。

    • 相比之下,任意 .NET 类型设置自动深度限制确实有意义,因为它们可能是深度过大的对象图,甚至可能包含循环引用;例如,Get-ChildItem | ConvertTo-Json可以迅速失控,其-Depth值低至4. 也就是说,通常不建议将任意 .NET 类型与 JSON 序列化一起使用:JSON并非旨在成为给定平台类型的通用序列化格式;相反,它专注于 DTO,仅包含属性,具有一组有限的数据类型

    • 请注意,嵌套集合(包括哈希表)本身不受深度限制,仅受其(标量)元素的限制。

    • 实际上,PowerShell 本身在幕后使用了 DTO 和其他类型之间的这种区别,即在远程后台作业的序列化上下文中。

  • -Depth然后只需要在指定深度故意截断输入对象树或序列化到更深的级别(如果需要,如果深度大于内部最大深度限制,则需要使用100

于 2018-12-04T06:26:48.707 回答