我需要自动将许多.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值动态访问和设置属性?
答案 0 :(得分:2)
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
* ,枚举LoaderOptimization
和字符串ConfigurationFile
属性设置为指定的非默认值。
*注意:简单方法的一个警告是,普通的PowerShell脚本规则用于转换布尔值,因此只有$null
,0
和空的被视为虚假,其他一切都是真实的。这意味着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,但我可以看到有人想要显式初始化,或者通过更改值而不是删除条目来切换单个赋值,并运行此意外行为。)
答案 1 :(得分:0)
我在XML中添加了一个带有Property子节点的Properties元素,并添加了这个脚本:
if ($businessObject.Properties) {
$businessObject.Properties.GetElementsByTagName("Property") | % {
$prop = $_
$pi = $obj.GetType().GetProperty($prop.Name)
$pi.SetValue($obj, $prop.Value, $null)
}
}