我正在使用String设置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不再是一个字符串?
谢谢!
答案 0 :(得分:6)
这是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>