如何将XML映射到PowerShell中对象的动态属性



我需要自动将许多. 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)
    }
}

相关内容

  • 没有找到相关文章

最新更新