【问题标题】:Why is PowerShell telling me a String is not a String? And only when calling String.Length, first为什么 PowerShell 告诉我字符串不是字符串?并且仅在调用 String.Length 时,首先
【发布时间】:2012-04-27 17:58:40
【问题描述】:

我正在使用字符串设置 XML 属性,PowerShell 告诉我“只有字符串可以用作设置 XmlNode 属性的值”。这是一个简单的例子。首先,我运行这个:

$xmlDoc = [xml]@"
<root>
  <ComponentRef Id="a" />
</root>
"@

$newId = "b"

$length = $newId.Length

Write-Host ("`n`$newId is a string, see: `$newId.GetType().FullName = " + $newId.GetType().FullName + "`n")
Write-Host ("Running `"`$xmlDoc.root.ComponentRef.Id = `$newId`"...`n")
$xmlDoc.root.ComponentRef.Id = $newId
Write-Host ("ComponentRef.Id is now: " + $xmlDoc.root.ComponentRef.Id)

对我来说,输出是:

$newId is a string, see: $newId.GetType().FullName = System.String

Running "$xmlDoc.root.ComponentRef.Id = $newId"...

Cannot set "Id" because only strings can be used as values to set XmlNode properties.
At D:\Build\Tools\mass processing\Untitled4.ps1:14 char:27
+ $xmlDoc.root.ComponentRef. <<<< Id = $newId
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

ComponentRef.Id is now: a

该错误消息一定是错误的。等号右侧的值一个字符串,如上面的输出所示。但它出错了,所以 XML 属性仍然读取“a”。现在它变得更奇怪了。让我们将调用 $newId.length 的行注释掉,然后观察它是否正常工作。

这样注释掉:#$length = $newId.Length。现在的输出是:

$newId is a string, see: $newId.GetType().FullName = System.String

Running "$xmlDoc.root.ComponentRef.Id = $newId"...

ComponentRef.Id is now: b

我不是要求修复,因为我知道如何通过转换为最后一个赋值运算符右侧的 [string] 来解决此问题。我想知道的是:

谁能解释为什么调用 $newId.Length(一个 getter!)会导致 PowerShell 认为 $newId 不再是一个字符串?

谢谢!

【问题讨论】:

  • 似乎问题出在如何从根 .NET 对象改编 PowerShell 对象(例如,在 C# 中您不能调用 xmlDoc.root - root 是 PS 添加的属性) .这对 PS 和您遇到的任何 .NET 对象应该是透明的,所以我不确定为什么这个对象会爆炸。例如 $xmlDoc.root.ComponentRef.SetAttribute("Id", $newId) 工作正常。很奇怪……
  • 一定是这样的。调用 Length 会触发它肯定很奇怪。让我觉得这是一个 PowerShell 错误。也许这就是答案,我们拭目以待;)我不确定 PowerShell 团队是否接受公众的错误报告。
  • JohnB 他们确实接受了错误报告 - 使用实时 ID 登录到 connect.microsoft.com/powershell 并使用 repro 记录错误。

标签: powershell


【解决方案1】:

这是 V2 扩展类型系统中的一个不幸错误,其中可以围绕基本 .NET 类型创建 psobject 包装器。当您向对象添加成员或访问不存在的属性时,可能会发生这种情况。当您访问对象 IIRC 上的 psobject 属性时,也会发生这种情况,例如$newId.psobject。当您留在 PowerShell 中时,这通常不会导致任何问题。

更新:调用 .NET 不是问题。一些快速的 .NET 测试代码表明它为属性设置器获取了一个未包装的对象。在使用Trace-Command 查看此内容后,它似乎是 PowerShell 的XmlNodeAdapater 中的一个错误:

DEBUG: ETS Information: 0 :  Method      Enter PSObject..ctor():object = System.Management.Automation.RuntimeException:
 Cannot set "Id" because only strings can be used as values to set XmlNode properties. --->
System.Management.Automation.SetValueException: Cannot set "Id" because only strings can be used as values to set
XmlNode properties.
   at System.Management.Automation.XmlNodeAdapter.PropertySet(PSProperty property, Object setValue, Boolean
convertIfPossible)
   at System.Management.Automation.Adapter.BasePropertySet(PSProperty property, Object setValue, Boolean convert)
   at System.Management.Automation.PSProperty.SetAdaptedValue(Object setValue, Boolean shouldConvert)
   at System.Management.Automation.PSProperty.set_Value(Object value)
   at System.Management.Automation.PropertyReferenceNode.SetValue(PSObject obj, Object property, Object value,
ExecutionContext context)
   --- End of inner exception stack trace ---
   at System.Management.Automation.PropertyReferenceNode.SetValue(PSObject obj, Object property, Object value,
ExecutionContext context)
   at System.Management.Automation.AssignablePropertyReference.SetValue(Object value, ExecutionContext context)
   at System.Management.Automation.AssignmentStatementNode.Execute(Array input, Pipe outputPipe, ExecutionContext
context)
   at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe
outputPipe, ArrayList& resultList, ExecutionContext context)

确保您始终获得base .NET 对象的一种方法是:

$xmlDoc.root.ComponentRef.Id = $newId.psobject.baseobject

好消息是这个问题已在 V3 中得到修复,例如:

PS> $xmlDoc = [xml]@"
<root>
  <ComponentRef Id="a" />
</root>
"@

PS> $newId = "b"

PS> $newId.Length
1

PS> $newId.psobject.typenames
System.String
System.Object

PS> $xmlDoc.root.ComponentRef.Id = $newId

PS> $xmlDoc | format-xml # From PowerShell Community Extensions
<root>
  <ComponentRef Id="b" />
</root>

【讨论】:

  • 可以理解是 XML 适配器导致了问题。这可以解释为什么依赖 .NET 方法 SetAttribute 有效: $xmlDoc.root.ComponentRef.SetAttribute("Id", $newId)
  • 我在 5.0 (windows 10) 中遇到了同样的问题。看来这个“功能”又冒出来了……
  • OP 的脚本在 5.0 build 5.0.10130.0 上适用于我。您正在运行哪个版本的 5.0?
  • 我在 Server 2016 (v5.1.14300.1000) 中得到了这个。我认为这与您在分配之前如何以交互方式访问变量有关,因为重新启动 shell 并重新运行脚本使其顺利进行。在此之前,我一直在以交互方式试验命令。似乎没有任何强制转换或 .ToString() 努力。
猜你喜欢
  • 2022-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-16
  • 2010-12-22
  • 2022-11-29
  • 2020-09-25
  • 1970-01-01
相关资源
最近更新 更多