【问题标题】:Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2意外的 ConvertTo-Json 结果?答案:它的默认 -Depth 为 2
【发布时间】:2019-05-04 03:39:21
【问题描述】:

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

元问题

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

重复

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

不同

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

【问题讨论】:

  • 我遇到了同样的问题。它让我问虽然 -Depth 参数有什么好处?这似乎引起了很多混乱......
  • @Omglolyes 这在评分最高的答案中得到了解决,本质上对象可以是自引用的,如果您尝试达到“最大”深度,则会创建一个无限循环。
  • 我认为你可以将my question添加到列表中。

标签: json powershell depth convertto-json


【解决方案1】:

回答

ConvertTo-Json 有一个 -Depth 参数:

指定包含的对象的级别数 JSON 表示。
默认值为2

示例

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

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

TL;DR

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

为什么?

这个问题也经常在另一个讨论中结束:为什么深度会受到限制?

某些对象具有循环引用,这意味着如果将子对象序列化为 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 自动设置为较大的值并不是一个好主意。

【讨论】:

  • 虽然,由于 JSON 不支持循环依赖,因此编写一个不支持循环依赖的序列化程序似乎比只走到一定深度的序列化程序更自然。 除了 PowerShell 中的所有主要 JSON 序列化程序都执行此操作。我会称之为“糟糕的设计决策”而不是“必要性”。
  • Powershell 7.1 会发出警告。 “如果 ConvertTo-Json 超过 -Depth 值,则发出警告 (#13692)”docs.microsoft.com/en-us/powershell/scripting/whats-new/…
【解决方案2】:

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


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

至于行为的正当理由

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

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

我创建了GitHub issue #8393,其中包含一个建议来改变当前的行为,具体如下

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

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

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

    • DTO 和其他类型之间的这种区别实际上是由 PowerShell 本身在幕后使用的,即在 remoting 的序列化上下文中em>后台工作

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

【讨论】:

  • 此深度参数值得获得 WTF 年度大奖。那个 GitHub 问题实在是太可悲了。人们怎么能争论这种行为的敏感性呢?这显然是错误的,他们用警告“修复”了它。哈哈!像往常一样使用 powershell 优化边缘情况。
  • 我听到了,@Skrymsli。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多