我需要自动将许多. net对象添加到业务系统中。PowerShell脚本需要读取XML输入文件,并通过业务系统的API执行适当的更改。
我发现的问题是对象有许多不同的类型,因此具有不同的属性
下面是一个XML示例:
<BusinessObject>
<Action>Add</Action>
<Id>{867B6C43-2A20-485D-A3E3-CBFCD50CA6F3}</Id>
<AssemblyName>ABC.BusinessObjects, Version=1.0.0.0, Culture=neutral, PublicKeyToken=361ad75badc53918</AssemblyName>
<ClassName>ABC.BusinessObjects.HealthService</ClassName>
<!-- Properties specific to object -->
<HealthServiceName>Jo's GP Super Center</HealthServiceName>
</BusinessObject>
<BusinessObject>
<Action>Add</Action>
<Id>{867B6C43-2A20-485D-A3E3-CBFCD50CA6F3}</Id>
<AssemblyName>ABC.BusinessObjects, Version=1.0.0.0, Culture=neutral, PublicKeyToken=361ad75badc53918</AssemblyName>
<ClassName>ABC.BusinessObjects.Patient</ClassName>
<!-- Properties specific to object -->
<PatientName>Anna Smith</PatientName>
</BusinessObject>
脚本相关部分:
$xmlItem.BusinessObjects.GetElementsByTagName("BusinessObject") | % {
$businessObject = $_
if ($businessObject.Action -eq "Add") {
$assemblyName = $businessObject.AssemblyName
$className = $businessObject.ClassName
$assembly = [Reflection.Assembly]::Load($assemblyName)
$obj = $assembly.CreateInstance($className)
### TODO: How to set properties on $obj ???
$api.AddBusinessObject($obj)
}
}
我可能需要将特定于对象的属性放入它们自己的XML元素中,以便我可以遍历它们。我不确定的是在这个循环中要做什么。
假设每个属性的XML元素名称与$obj属性名称匹配,我如何从其相应的XML值动态访问和设置属性?
您使用PropertyInfo.SetValue
的方法当然可以工作,但是您也可以利用PowerShell的动态特性和自动类型转换来大大简化初始化:
$businessObject.Properties.GetElementsByTagName( 'Property' ) | % {
$obj.($_.Name) = $_.Value
}
在这里,PowerShell将计算($_.Name)
的值,然后用该值调用$obj.___
,就像您编写$obj.SomeProperty
一样。它还将$_.Value
返回的字符串转换为适当的类型(如下所示)∗,而SetValue
将抛出一个参数异常。
与现有代码结合:
$xmlItem.BusinessObjects.GetElementsByTagName( 'BusinessObject' ) | % {
$businessObject = $_
if( $businessObject.Action -eq 'Add' ) {
$assemblyName = $businessObject.AssemblyName
$className = $businessObject.ClassName
$assembly = [Reflection.Assembly]::Load( $assemblyName )
$obj = $assembly.CreateInstance( $className )
if( $businessObject.Properties ) {
$businessObject.Properties.GetElementsByTagName( 'Property' ) | % {
$obj.($_.Name) = $_.Value
}
}
$obj # $api.AddBusinessObject( $obj )
}
}
我使用以下xml测试了这一点,改编自问题的示例(AppDomainSetup是一个方便的系统类型,具有许多可设置的属性):
<BusinessObjects>
<BusinessObject>
<Action>Add</Action>
<Id>{867B6C43-2A20-485D-A3E3-CBFCD50CA6F3}</Id>
<AssemblyName>mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</AssemblyName>
<ClassName>System.AppDomainSetup</ClassName>
<Properties>
<Property Name="DisallowCodeDownload" Value="True"/>
<Property Name="LoaderOptimization" Value="SingleDomain"/>
<Property Name="ConfigurationFile" Value="C:config.xml"/>
</Properties>
</BusinessObject>
</BusinessObjects>
将布尔值DisallowCodeDownload
∗、enum属性LoaderOptimization
和字符串属性ConfigurationFile
设置为指定的非默认值。
∗注意:简单方法的一个警告是,正常的PowerShell脚本规则用于转换布尔值,因此只有$null
, 0
和empty被视为假,其他所有内容都被视为真。这意味着xml中的Value="False"
设置将仍然将属性设置为true,因为PowerShell看到一个非空字符串'False'
,并且不会进一步解析它。
您可以使用空字符串Value=""
将属性设置为false。然而,为了允许更直观的解析行为,您需要手动调用[Convert]::ToBoolean( $_.Value )
,或[Convert]::ChangeType( $_.Value, [bool] )
,或类似的东西:
$name = $_.Name
if( $obj.$name -is [bool] ) {
$obj.$name = [Convert]::ToBoolean( $_.Value )
} else {
$obj.$name = $_.Value
}
(布尔属性的默认值通常为false,但我可以看到有人想要显式初始化,或者通过改变值而不是删除条目来切换单个赋值,并遇到这种意外行为。)
我在XML中添加了一个带有Property子节点的Properties元素,并添加了以下脚本:
if ($businessObject.Properties) {
$businessObject.Properties.GetElementsByTagName("Property") | % {
$prop = $_
$pi = $obj.GetType().GetProperty($prop.Name)
$pi.SetValue($obj, $prop.Value, $null)
}
}