了解 PowerShell 中的 NewtonSoft

2023-12-03

我涉足 JSON 解析和 NewtonSoft 的世界,至少可以说我很困惑。

采用以下 PowerShell 脚本:

$json = @"
{
    "Array1": [
        "I am string 1 from array1",
        "I am string 2 from array1"
    ],   

    "Array2": [
        {
           "Array2Object1Str1": "Object in list, string 1",
           "Array2Object1Str2": "Object in list, string 2"
        }
    ]

}
"@

#The newtonSoft way
$nsObj = [Newtonsoft.Json.JsonConvert]::DeserializeObject($json, [Newtonsoft.Json.Linq.JObject])

$nsObj.GetType().fullname #Type = Newtonsoft.Json.Linq.JObject

$nsObj[0] #Returns nothing. Why?

$nsObj.Array1 #Again nothing. Maybe because it contains no key:value pairs?
$nsObj.Array2 #This does return, maybe because has object with kv pairs

$nsObj.Array2[0].Array2Object1Str1 #Returns nothing. Why? but...
$nsObj.Array2[0].Array2Object1Str1.ToString() #Cool. I get the string this way.

$nsObj.Array2[0] #1st object has a Path property of "Array2[0].Array2Object1Str1" Great!

foreach( $o in $nsObj.Array2[0].GetEnumerator() ){
    "Path is: $($o.Path)"
    "Parent is: $($o.Parent)"
} #??? Why can't I see the Path property like when just output $nsObj.Array2[0] ???
#How can I find out what the root parent (Array2) is for a property? Is property even the right word?

我希望能够找到任何给定位置的根父级的名称。如上所述,我想知道我正在查看的项目 (Array2Object1Str1) 属于 Array2 根父级。

我想我不理解这里的一些基本原理。是否可以确定根父级?另外,如果能帮助理解我在脚本中的评论,那就太好了。这就是为什么我无法返回路径或父级等内容,但在 VSCode 中调试时可以看到它。


dbc 的回答包含有用的背景信息,并清楚地表明从 PowerShell 调用 NewtonSoft Json.NET 库很麻烦。

Given PowerShell's built-in support for JSON parsing - via the ConvertFrom-Json and ConvertTo-Json cmdlets - there is usually no reason to resort to third-party libraries (directly[1]), except in the following cases:

  • When 表现是最重要的。
  • 当。。。的时候PowerShell JSON 解析的局限性必须克服(缺乏对空键名称和仅字母大小写不同的键的支持)。
  • 当你需要使用 Json.NET 类型及其方法,而不是无方法的“财产袋”[pscustomobject]实例ConvertFrom-Json结构体。

直接在 PowerShell 中使用 NewtonSoft 的 Json.NET 是awkward, it is 易于管理的,如果你观察一些rules:

  • Lack of visible输出没有一定意味着根本没有任何输出:

    • 由于一个bug在 PowerShell 中(从 v7.0.0-preview.4 开始),[JValue]实例和[JProperty]包含它们的实例产生无可见输出默认情况下; 访问他们的(强类型).Value财产代替 (e.g., $nsObj.Array1[0].Value or $nsProp.Value.Value (sic))

    • To 输出string表示 of a [JObject] / [JArray] / [JProperty] / [JValue]例如,不要依赖原样输出(例如,$nsObj), 使用显式字符串化.ToString() (e.g., $nsObj.ToString());而字符串插值(例如,"$nsObj") does 一般来说工作,它不与[JValue]实例,由于上述错误。

    • [JObject] and [JArray]对象默认显示其列表elements' 实例属性(隐含Format-List应用于枚举的对象);你可以use the Format-*用于调整输出的 cmdlet; e.g., $nsObj | Format-Table Path, Type.

      • Due to 另一个错误(可能具有相同的根本原因),从 PowerShell Core 7.0.0-preview.4 开始,默认输出为[JObject]实例实际上是broken如果输入 JSON 包含array(打印错误format-default : Target type System.Collections.IEnumerator is not a value type or a non-abstract class. (Parameter 'targetType')).
  • 以数字方式索引到[JObject]实例,即访问属性通过index不要使用名称,而是使用以下惯用语:@($nsObj)[<n>], where <n>是感兴趣的数字索引。

    • $nsObj[<n>]实际上should有效,因为与 C# 不同,PowerShell 公开了实现的成员通过接口作为可直接调用的类型成员,因此numeric索引器JObject通过实施IList<JToken>接口应该是可访问的,但实际上却不是,大概是因为this bug(从 PowerShell Core 7.0.0-preview.4 开始)。

    • 解决方法基于@(...),PowerShell 的数组子表达式运算符,强制枚举[JObject]实例产生其数组[JProperty]成员,然后可以通过索引访问;请注意,这种方法是simple, but 效率不高,因为 aux 的枚举和构造。数组出现;然而,考虑到单个 JSON 对象(而不是array)通常不具有大量属性,这在实践中不太重要。
      基于反射的解决方案,可访问IList<JToken>接口的数字索引器是可能的,但甚至可能更慢。

    • 请注意,额外的.Value可能再次需要基于访问print结果(或提取强类型属性value).

  • 一般来说,不要使用.GetEnumerator() method; [JObject] and [JArray]实例是directly可枚举的.

    • 请记住PowerShell 可能自动地列举这样的例子在你意想不到的情况下,特别是在pipeline;尤其,当你发送一个[JObject]对于管道来说,它是它的组成部分[JProperty]而是单独发送的.
  • 使用类似的东西@($nsObj.Array1).Value来提取values的数组的原始JSON 值(字符串、数字……) - 即,[JValue]实例 - 作为数组。

下面在上下文中演示了这些技术:

$json = @"
{
    "Array1": [
        "I am string 1 from array1",
        "I am string 2 from array1",
    ],

    "Array2": [
        {
           "Array2Object1Str1": "Object in list, string 1",
           "Array2Object1Str2": "Object in list, string 2"
        }
    ]

}
"@

# Deserialize the JSON text into a hierarchy of nested objects.
# Note: You can omit the target type to let Newtonsoft.Json infer a suitable one.
$nsObj = [Newtonsoft.Json.JsonConvert]::DeserializeObject($json)
# Alternatively, you could more simply use:
#   $nsObj = [Newtonsoft.Json.Linq.JObject]::Parse($json)

# Access the 1st property *as a whole* by *index* (index 0).
@($nsObj)[0].ToString()

# Ditto, with (the typically used) access by property *name*.
$nsObj.Array1.ToString()

# Access a property *value* by name.
$nsObj.Array1[0].Value

# Get an *array* of the *values* in .Array1.
# Note: This assumes that the array elements are JSON primitives ([JValue] instances.
@($nsObj.Array1).Value

# Access a property value of the object contained in .Array2's first element by name:
$nsObj.Array2[0].Array2Object1Str1.Value


# Enumerate the properties of the object contained in .Array2's first element
# Do NOT use .GetEnumerator() here - enumerate the array *itself*
foreach($o in $nsObj.Array2[0]){
  "Path is: $($o.Path)"
  "Parent is: $($o.Parent.ToString())"
}

[1] PowerShell Core - but not Windows PowerShell - currently (v7) actually uses NewtonSoft's Json.NET behind the scenes.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解 PowerShell 中的 NewtonSoft 的相关文章

随机推荐