6

文字列を使用して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(ゲッター!)を呼び出すと、PowerShellが$ newIdが文字列ではなくなったと見なす可能性がある理由を誰かが説明できますか?

ありがとう!

4

1 に答える 1

6

これは、V2 拡張型システムの不幸なバグでありpsobject、ベース .NET 型の周りにラッパーが作成される可能性があります。これは、オブジェクトにメンバーを追加したとき、または存在しないプロパティにアクセスしたときに発生する可能性があります。psobjectオブジェクト IIRCのプロパティにアクセスするときにも発生する可能性があります$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 オブジェクトを常に取得する方法の 1 つは、次のとおりです。

$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>
于 2012-04-27T23:19:18.917 に答える