【问题标题】:Issue with Newtonsoft Json Linq in Powershell vs .NET?Powershell vs .NET 中的 Newtonsoft Json Linq 问题?
【发布时间】:2018-11-08 18:53:59
【问题描述】:

我在 C# .NET 应用程序中有以下代码......

 JObject myJObject = new JObject();
 JArray myJArray = new JArray();

 myJObject.Add(
                (new JProperty("PropA", (new JObject(new JProperty("PropA1", "")))))
              );

 Console.WriteLine(myJObject.ToString());

它按预期工作,我得到的输出是

{ “PropA”:{ “PropA1”:“” } }

但是,当我在 Power shell 中执行完全相同的代码时(但已翻译)...

using namespace Newtonsoft.Json.Linq
Add-Type -Path "C:\Temp\Newtonsoft.Json.dll" 

[JObject] $myJObject = New-Object JObject
[JArray]  $myJArray = New-Object JArray

$myJObject.Add(
                (New-Object JProperty("PropA",(New-Object JObject((New-Object JProperty("PropA1",""))))))
              ) 

write-host $myJObject.ToString()       

它爆炸了,我得到了错误..

New-Object:使用“1”参数调用“.ctor”的异常:“无法将 Newtonsoft.Json.Linq.JValue 添加到 Newtonsoft.Json.Linq.JObject。”

很有趣,如果我使用相同的代码但添加 2 个属性它可以工作...

  $myJObject.Add(
                 (New-Object JProperty("PropA",(New-Object JObject((New-Object JProperty("PropA1","")), (New-Object JProperty("PropA2",""))))))
                 ) 

但我当然明白...

{ “PropA”:{ “PropA1”:“”, “PropA2”:“” } }

我做错了什么?

【问题讨论】:

    标签: powershell json.net


    【解决方案1】:

    tl;dr

    using namespace Newtonsoft.Json.Linq
    Add-Type -Path "C:\Temp\Newtonsoft.Json.dll" 
    
    [JObject] $myJObject = New-Object JObject
    
    $myJObject.Add(
      (New-Object JProperty "PropA",
                            (New-Object JObject (
                                # Note the , (...) around this New-Object call,
                                # which wraps the resulting JProperty instance in a 
                                # single-element array.    
                                , (New-Object JProperty "PropA1", "")
                              )
                            )
      )
    )
    
    $myJObject.ToString() 
    

    替代方法,使用 PSv5+ 静态 ::new() 方法可用于调用构造函数的类型:

    [JObject] $myJObject = New-Object JObject
    
    $myJObject.Add(
      [JProperty]::new("PropA",
        [JObject]::new(
          # No workaround needed.
          [JProperty]::new("PropA1", "")
        )
      )
    )
    
    $myJObject.ToString() 
    

    Brian Rogers' answer 在正确的轨道上:问题在于 内部的 JObject 构造函数没有接收到作为参数传递的 JProperty 实例 strong> 当使用New-Object cmdlet 调用时。

    # *Seemingly* the equivalent of: new JObject(new JProperty("PropA1", ""))
    New-Object JObject (New-Object JProperty "PropA1", "") # !! FAILS
    

    注意使用 shell-like 语法 - 空格分隔的参数,参数列表周围没有括号 - 因为这是诸如 New-Object 之类的 cmdlet 所期望的 -这些是不是方法调用;它们是在 argument mode 中解析的 PowerShell 命令。

    JProperty 实例包装在 helper 数组中可以解决问题

    New-Object JObject (, (New-Object JProperty "PropA1", "")) # OK - wrapped in array
    

    , 是 PowerShell 的 数组构造运算符,因此 , <expr> 创建一个包含 <expr> 的结果的单元素 object[] 数组。[1]支持>

    不过,这个问题可以通过使用PSv5+静态::new()构造方法一开始就避免::new() 适用于所有类型,并允许您使用方法语法调用构造函数:

    [JObject]::new([JProperty]::new("PropA1", "")) # OK
    

    至于 为什么 标量 JProperty 参数不适用于 PowerShell 中的 New-Object

    由于JProperty 类型实现了IEnumerable 接口,PowerShell 尝试枚举单个JProperty 实例,而不是将其作为自身传递 (隐含)-ArgumentList 参数(它本身就是object[]-typed)。
    这失败了,因为 JObject 构造函数随后看到了该枚举的结果,这是一个代表 JSON 属性 valueJValue 实例,并从 JValue 实例构造了一个 JObject 实例不允许,如错误消息中所反映的 - 请参阅Brian's answer here

    将这样的实例包装在一个数组中可以绕过这个问题:它会阻止枚举JProperty 实例并安全地将其传递到辅助object[] 实例中。

    如果您传递给New-Object JObject 的是一个数组开头,例如您传递两个 JProperty 实例的示例,问题还在于避免。

    另请注意,转换为 [JObject] 也可以,但仅适用于 one 属性:

    New-Object JObject ([JObject] (New-Object JProperty "PropA1", "")) # OK with 1 prop.
    

    并不是说 Powershell 有内置的 JSON 支持,它允许将哈希表和自定义对象方便地转换为 JSON 并返回:

    # Convert a nested hashtable to JSON
    PS> @{ PropA = @{ PropA1 = 42 } } | ConvertTo-Json -Compress
    {"PropA":{"PropA1":42}}
    
    # Convert JSON to a custom object [pscustomobject] with property
    # .PropA whose value is another custom object, with property .PropA1
    PS> '{"PropA":{"PropA1":42}}' | ConvertFrom-Json
    
    PropA
    -----
    @{PropA1=42}
    

    因此,除非您有特殊要求和/或性能问题,否则 PowerShell 的内置功能可能就足够了,您甚至可以通过 JSON 字符串表示将自定义对象 / 哈希表转换为 JObject / JToken 实例,尽管不便宜:

    PS> [JToken]::Parse((@{ PropA = @{ PropA1 = 42 } } | ConvertTo-Json -Compress)).ToString()
    {
      "PropA": {
        "PropA1": 42
      }
    }
    

    [1] 请注意,数组子表达式运算符 @(...) 在这种情况下 not 工作得很好,因为它也涉及到不需要的 @987654363 的 枚举 @instance 在将结果包装到 [object[]] 数组之前:

    # !! FAILS too: 
    # The JProperty instance is again enumerated, resulting in a single
    # JValue instance, which is what @(...) then wraps in a
    # single-element [object[]] array.
    $var = New-Object JProperty "PropA1", ""
    New-Object JObject @($var)
    

    奇怪的是,你可以摆脱@(...) 如果你直接在其中调用New-Object

    # !! Happens to work, but is OBSCURE.
    # !! Equivalent of: (, (New-Object JProperty "PropA1", "")) 
    New-Object JObject @(New-Object JProperty "PropA1", "")
    

    @(...) 阻止 在这种情况下枚举是由于 command 输出(例如,@(New-Object ...))而不是 表达式 输出(例如,@($var))- 不是@(...) 枚举的;也就是说,如果命令输出的东西是可枚举的/集合作为一个整体 - 这是 New-Object 在实例化此类类型时所做的 - @(...) 将其按原样包装在单个元素中[object[]] 数组。 (顺便说一句:$(...) 导致枚举)。

    【讨论】:

      【解决方案2】:

      我怀疑 PowerShell 出于某种原因没有解析正确的 JObject 构造函数,但我不知道为什么。我使用您的示例玩了一段时间,但无法哄骗 PowerShell 做正确的事情。

      我建议重写您的脚本,以便使用空构造函数创建JObjects,然后使用Add 方法向它们添加属性。我能够通过这种方式获得您想要的输出:

      $emptyVal = New-Object JValue ""
      $innerObject = New-Object JObject
      $innerObject.Add("PropA1", $emptyVal)
      $myJObject = New-Object JObject
      $myJObject.Add("PropA", $innerObject)
      
      Write-Host $myJObject.ToString()
      

      【讨论】:

      • 感谢您的尝试。我很高兴你看到了和我一样的东西,因为有时候我觉得我快疯了!我发现的另一种解决方法是同时添加两者,以便获得正确的 OverLoad,然后返回并删除第二个属性,这样最终结果就是我想要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多