我在从文档中选择某些特定的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。布局如下所示:
答案 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
注释
.
开头以引用该节点。ForEach-Object
主体如何生成一个对象数组,而无需将它们添加到临时变量中。这就是$allAttendees
的作业如何在上面发挥作用。"...$($attendee.eventuserid)..."
构造对您不熟悉。.InnerText
是多余的。 Powershell将自动为您完成。