2011-11-22 39 views
1

我需要自动将许多.NET对象添加到业务系统中。 PowerShell脚本需要读取XML输入文件并通过业务系统的API执行适当的更改。如何将XML映射到PowerShell中的对象上的动态属性?

我发现的问题是,对象有许多不同的类型,因此具有不同的属性

这里是XML的例子:

$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元素,这样我可以循环:

<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> 

脚本的相关部分通过他们。我不确定的是在该循环内部要做什么。

假设每个属性的XML元素名称都与$ obj属性名称相匹配,那么如何动态访问并设置相应XML值的属性?

回答

2

Your approach,但你也可以利用PowerShell的动态特性和自动类型转换大大简化初始化:

$businessObject.Properties.GetElementsByTagName('Property') | % { 
    $obj.($_.Name) = $_.Value 
} 

这里,PowerShell的将评估​​值,然后调用$obj.___与价值,就像如果你有w^ritten $obj.SomeProperty。它还会将由$_.Value返回的字符串转换为适当的类型(如下所示)&lowast;,而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&lt;,枚举LoaderOptimization和字符串ConfigurationFile属性为指定的非默认值。


&lowast;注意:一个简单的方法是使用普通的PowerShell脚本规则来转换布尔值,所以只有$null,0和empty被视为false,其他所有的都被视为true。这意味着一个Value="False" xml设置将仍然设置属性为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 
} 

(布尔属性通常有假的默认值,但我可以看到有人想明确初始化或切换单个分配,方法是更改​​值而不是删除条目,并运行此意外行为。)

0

我添加属性子节点属性元素的XML,并将此脚本:使用PropertyInfo.SetValue肯定会工作

if ($businessObject.Properties) { 
    $businessObject.Properties.GetElementsByTagName("Property") | % { 
     $prop = $_ 
     $pi = $obj.GetType().GetProperty($prop.Name) 
     $pi.SetValue($obj, $prop.Value, $null) 
    } 
} 
相关问题