我在C#.NET App中具有以下代码。...
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()
它炸毁,我得到了错误。
新对象:使用“ 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”:“” } }
我在做什么错了?
答案 0 :(得分:4)
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
实例作为参数传递 (当使用New-Object
cmdlet调用时。
# *Seemingly* the equivalent of: new JObject(new JProperty("PropA1", ""))
New-Object JObject (New-Object JProperty "PropA1", "") # !! FAILS
请注意使用类似于shell的语法-空格分隔的参数,参数列表周围没有括号-因为这就是New-Object
这样的cmdlet期望-这些是 not 方法调用;它们是argument mode中解析的PowerShell命令。
将JProperty
实例包装在 helper数组中可解决此问题:
New-Object JObject (, (New-Object JProperty "PropA1", "")) # OK - wrapped in array
,
是PowerShell的 array-construction操作符,因此, <expr>
创建了一个包含object[]
的结果的单元素<expr>
数组。 [1]
但是,可以通过使用PSv5 +静态::new()
方法进行构建来避免问题; ::new()
适用于所有类型,并允许您使用方法语法:
[JObject]::new([JProperty]::new("PropA1", "")) # OK
关于为什么 标量 JProperty
自变量不适用于PowerShell中的New-Object
:>
由于JProperty
类型实现了IEnumerable
接口,因此PowerShell尝试枚举单个JProperty
实例,而不是将其本身传递当绑定到(隐含的)-ArgumentList
参数(本身是object[]
类型的参数)时。
之所以失败,是因为JObject
构造函数随后会看到该枚举的结果,该枚举是一个表示JSON属性 value 的JValue
实例,并构造了一个JObject
实例如错误消息所示,不允许从JValue
实例中访问-请参见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]请注意,数组子表达式操作符@(...)
在这种情况下不能正常运行,因为它也涉及到不必要的枚举。 JProperty
实例,然后将结果包装在[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 BEST AVOIDED.
# !! Use (, (New-Object JProperty "PropA1", "")) instead.
New-Object JObject @(New-Object JProperty "PropA1", "")
这次幸运事故的高度模糊原因-不应该依赖-使用 command 而不是 expression 会直接导致通常看不见的额外[psobject]
包装器,在这种情况下是为了防止对JProperty
实例进行枚举;有关一般背景信息,请参见this GitHub issue。
答案 1 :(得分:3)
我怀疑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()