如何在缺少某些属性时将XML转换为对象?

时间:2013-10-01 11:52:18

标签: xml powershell

问题

我正在尝试将XML位置列表转换为一组对象。

XML文件每个位置都有一个Location元素。 XML属性包含对象属性值。

我希望某些目标属性与源属性具有不同的名称。

稍后我想将对象序列化为CSV文件,因此属性的顺序很重要。

以下是两个位置的示例,用于说明问题:

[xml] $InputXml = @'
<?xml version="1.0" encoding="UTF-8"?>

<CTLocations xmlns="http://www.cartrawler.com/">
<Country code="AL" name="Albania" continent="Europe">
<Location Id="7188" Name="Tirana Airport" Lat="41.42108838" Lng="19.71271276" CountryCode="AL" Address="Tirana Airport Muhamet Gjollesha Str., Muhamet Gjollesha Str., Tirana" CityName="Tirana" Airport="1" AirportCode="TIA" RailwayStation="0"/>
<Location Id="30768" Name="Tirana Downtown" Lat="41.332" Lng="19.832" CountryCode="AL" Address="Rruga E Durresit. Nr 61, Tirana" CityName="Tirana" Airport="0" RailwayStation="0"/>
</Country>
</CTLocations>
'@

部分解决方案

我的解决方案适用于具有完整属性集的元素,但在缺少任何属性时失败。

我使用Select-Xml cmdlet选择重要元素,使用PSCustomObject创建具有有序重命名属性的对象。

Select-Xml -Xml $InputXml -XPath '//ns:Location' -Namespace @{ns = 'http://www.cartrawler.com/'} |
% {
  $n = $_.Node

  [PSCustomObject] @{
    LocationCode = $n.Id
    LocationName = $n.Name
    Latitude = $n.Lat
    Longitude = $n.Lng
    CountryCode = $n.CountryCode
    FormattedAddress = $n.Address
    CityName = $n.CityName
    ServesAirport = $n.Airport
    AirportCode = $n.AirportCode
    ServesRailwayStation = $n.RailwayStation
  }
}

严格模式结果

当我有严格模式设置时:

Set-StrictMode -Version 3.0

第一个元素包含所有属性,因此转换为对象:

LocationCode         : 7188
LocationName         : Tirana Airport
Latitude             : 41.42108838
Longitude            : 19.71271276
CountryCode          : AL
FormattedAddress     : Tirana Airport Muhamet Gjollesha Str., Muhamet Gjollesha Str., Tirana
CityName             : Tirana
ServesAirport        : 1
AirportCode          : TIA
ServesRailwayStation : 0

第二个元素缺少AirportCode属性,因此PowerShell会引发异常:

Property 'AirportCode' cannot be found on this object. Make sure that it exists.
At line:16 char:3
+   [PSCustomObject] @{
+   ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], PropertyNotFoundException
    + FullyQualifiedErrorId : PropertyNotFoundStrict

非严格模式结果

当我关闭严格模式时:

Set-StrictMode -Off

PowerShell将这两个位置转换为对象。缺少的属性为null:

LocationCode         : 7188
LocationName         : Tirana Airport
Latitude             : 41.42108838
Longitude            : 19.71271276
CountryCode          : AL
FormattedAddress     : Tirana Airport Muhamet Gjollesha Str., Muhamet Gjollesha Str., Tirana
CityName             : Tirana
ServesAirport        : 1
AirportCode          : TIA
ServesRailwayStation : 0

LocationCode         : 30768
LocationName         : Tirana Downtown
Latitude             : 41.332
Longitude            : 19.832
CountryCode          : AL
FormattedAddress     : Rruga E Durresit. Nr 61, Tirana
CityName             : Tirana
ServesAirport        : 0
AirportCode          : 
ServesRailwayStation : 0

有更好的方法吗?

如果有更好的方法,我不想关闭严格模式以使其工作。

任何在严格模式下工作的东西都是可以接受的。我一直在寻找类似T-SQL CASE expressionPython's dict get method的内容。也许有一种XPath方法可以做到这一点。

1 个答案:

答案 0 :(得分:1)

在你的情况下很容易:

select-Xml -Xml $InputXml -XPath '//ns:Location' -Namespace @{ns = 'http://www.cartrawler.com/'} |
% {
  $n = $_.Node

  [PSCustomObject] @{
    LocationCode = $n.Id
    LocationName = $n.Name
    Latitude = $n.Lat
    Longitude = $n.Lng
    CountryCode = $n.CountryCode
    FormattedAddress = $n.Address
    CityName = $n.CityName
    ServesAirport = $n.Airport
    AirportCode = $(if($n.Airport -eq '1'){$n.AirportCode}else{""})
    ServesRailwayStation = $n.RailwayStation
  }
}

更多属:

select-Xml -Xml $InputXml -XPath '//ns:Location' -Namespace @{ns = 'http://www.cartrawler.com/'} |
% {
  $n = $_.Node

  [PSCustomObject] @{
    LocationCode = $n.Id
    LocationName = $n.Name
    Latitude = $n.Lat
    Longitude = $n.Lng
    CountryCode = $n.CountryCode
    FormattedAddress = $n.Address
    CityName = $n.CityName
    ServesAirport = $n.Airport
    AirportCode = $(if($n.GetAttributeNode("AirportCode") -ne $null){$n.AirportCode}else{""})
    ServesRailwayStation = $n.RailwayStation
  }
}

一旦写完这篇文章,我的老编程意见(你不关心的那种)就是如果你想使用严格模式就停止编写脚本并开始编写java,C#,C或C ++。你使用严格模式进行的近似是一种脚本编写,这对我来说非常好地解决了桌面角落的问题。我的观点是,使用严格模式会带来传统编程的所有缺点而没有兴趣,但这只是我的观点。