选择特定节点属性

时间:2017-12-28 11:15:07

标签: xml powershell

我在从文档中选择某些特定的XML数据时遇到了一些麻烦。 基础数据是营销活动。每个文档可以有多个事件。每个活动的内部都有多名与会者和注册人。 我开始在SelectNodes()循环中使用foreach并在转换为CSV之前将其读入哈希表。

对于单个事件,这似乎工作正常,但对于多个事件,行不一致,偶数与其他记录数据不同步。 我现在考虑将整个XML导出为CSV并让ETL工具从那里获得控制权。

我的理解存在差距,并且想知道是否有人知道如何在CSV中选择多个特定的XML属性,但它保持了序列的顺序。

我的PowerShell代码:

cls
[xml]$xml = Get-Content ("D:\sample.xml")

$dataTable  = @()
$eventNodes = $xml.SelectNodes('//event')
foreach ($event in $eventNodes) {
    $eventid    = $event.eventid
    $eventtitle = $event.eventtitle.InnerText               
    $eventtime  = $event.eventtime                           

    # get registrant data
    $registrantNodes = $xml.SelectNodes('//registrant')
    foreach ($registrant in $registrantNodes) {
        $firstname = $registrant.firstname.InnerText
        $lastname  = $registrant.lastname.InnerText
        $city      = $registrant.city.InnerText
        $state     = $registrant.state.InnerText    
        $country   = $registrant.country.InnerText
        $company   = $registrant.company.InnerText
        $workphone = $registrant.workphone.InnerText    
        $email     = $registrant.email.InnerText

        # get attendee data
        $attendeeNodes = $xml.SelectNodes('//attendee')
        foreach ($attendee in $attendeeNodes) {
            $attendedlive    = $attendee.attendedlive.InnerText
            $attendedarchive = $attendee.attendedarchive.InnerText

            # put all data into holding table
            $dataEntry = New-Object PSObject -Property @{
                FirstName       = $firstname;
                LastName        = $lastname;
                City            = $city;
                State           = $state;
                Country         = $country;
                Company         = $company;
                WorkPhone       = $workphone;
                Email           = $email;
                AttendedLive    = $attendedlive;
                AttendedArchive = $attendedarchive;
                EventID         = $eventid;
                EventTitle      = $eventtitle;
                EventTime       = $eventtime;
                Orginization    = 'North America';
            }
            $dataTable += $dataEntry
        }
    }
}

# display holding table
$dataTable

$dataTable | Export-Csv -Force -Path "D:\output.csv" -NoTypeInformation

我上传了一个示例XML文件here。布局如下所示:

XML layout

1 个答案:

答案 0 :(得分:1)

您的主要问题是所有XPath都是绝对路径 - 它们都从文档的根开始。当您查询//registrant时,XML文档不会神奇地为您提供属于您认为的当前"的所有注册人。这个特定代码行上的事件。它会为所有活动提供所有注册人,因为这是您所要求的。如果想要相对结果,请使用相对导航,即以当前元素(XPath中为.)开头的XPath。如本例所示。

您的第二个问题是注册人和与会者之间的关联是eventuserid。您无法简单地查询任何注册人,您必须考虑该ID才能选择正确的ID。您的代码不会这样做,幸运的是它在XPath中非常简单。

你的第三个问题是你自上而下看整个任务。活动 - 注册人 - 与会者。这就是你的XML的结构,但实际你想要每个与会者的CSV输出一行,并为该人提供一些相关数据。因此,自下而上这样做是明智的:首先是与会者,然后是相应的注册人和事件。

考虑以下代码:

cls

$xml = New-Object xml
$xml.Load("D:\sample.xml")

$allAttendees = $xml.SelectNodes('//attendee') | ForEach-Object {
    $attendee = $_
    $event = $attendee.SelectSingleNode('./ancestor::event[1]')
    $registrant = $event.SelectSingleNode("./registrants/registrant[eventuserid = '$($attendee.eventuserid)']")
    New-Object PSObject -Property @{
        FirstName       = $registrant.firstname
        LastName        = $registrant.lastname
        City            = $registrant.city
        State           = $registrant.state
        Country         = $registrant.country
        Company         = $registrant.company
        WorkPhone       = $registrant.workphone
        Email           = $registrant.email
        AttendedLive    = $attendee.attendedlive
        AttendedArchive = $attendee.attendedarchive
        EventID         = $event.eventid;
        EventTitle      = $event.eventtitle
        EventTime       = $event.eventtime
        Orginization    = 'North America';
    }
}

$allAttendees | Export-Csv -Force -Path "D:\output.csv" -NoTypeInformation

注释

  • 在某个节点调用所有XPath,并以.开头以引用该节点。
  • 您在PowerShell中的脚本块内生成并且不存储在变量中的所有值都将成为该脚本块的返回值的一部分。这就是ForEach-Object主体如何生成一个对象数组,而无需将它们添加到临时变量中。这就是$allAttendees的作业如何在上面发挥作用。
  • 要阅读的内容:XPath谓词和XPath轴以及字符串插值如何在Powershell中工作,以防"...$($attendee.eventuserid)..."构造对您不熟悉。
  • 明确使用.InnerText是多余的。 Powershell将自动为您完成。