2017-12-28 21 views
0

文書から特定のXMLデータを選択する際に問題があります。 基礎となるデータはマーケティングイベントです。ドキュメントごとに複数のイベントが存在する可能性があります。各イベントの中には複数の出席者と登録者がいます。 foreachループ内のSelectNodes()を使い始め、ハッシュテーブルに読み込んでからCSVに変換しました。特定のノードプロパティを選択してください

これはうまくいきましたが、複数のイベントでは行が矛盾していました。イベントIDは他のレコードデータと同期していませんでした。 私は現在、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

このようにXMLファイルをロードしないでください。 PowerShellでXMLファイルをロードする正しい方法は、 '$ xml = New-Object xml; $ xml.Load($ path) 'となります。このようにすると、XMLファイルのエンコーディングが自動的に検出されます。 'Get-Content'を使うと、ファイルのエンコーディングが' Get-Content'のデフォルトと一致しないときにデータを破壊します。それはスマートではない指交差と同等です。 – Tomalak

答えて

1

あなたの主な問題は、すべてのあなたのXPathが絶対パスであるということです - 彼らはすべてのドキュメントのルートから始まります。 //registrantを照会すると、XML文書は、この特定のコード行で「現在の」イベントと考えられるものに属するすべての登録者を魔法のように与えることはありません。それはあなたにあなたが求めているので、すべてすべてのイベントの登録者を与えるでしょう。この場合のように相対的な結果が必要な場合は、現在の要素(XPathの場合は.)から始まる相対ナビゲーション、つまりXPathを使用します。

あなたの二次的な問題は、登録者と出席者がeventuseridによって互いに関連していることです。単に登録者を照会することはできません。正しいIDを選択するには、そのIDを考慮に入れる必要があります。あなたのコードはそれをしません、幸いにもXPathでは非常に簡単です。

3番目の問題は、タスク全体をトップダウンで見ていることです。イベント - 登録者 - 出席者。あなたのXMLは構造化されていますが、実際にはあなたのCSVには1人の出席者あたり1つの出力行があり、その人の関連データがいくつかあります。それで、このボトムアップを行うことは賢明です。最初に出席者、次に登録者とイベントに従ってください。

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本体がtemp変数にオブジェクトを追加することなくオブジェクトの配列を生成する方法です。これは上記の$allAttendeesへの割り当て方法です。
  • about:XPathの述語とXPathの軸、および"...$($attendee.eventuserid)..."の構造があなたによく知られていない場合、Powershellでの文字列の補間の仕組み
  • .InnerTextを明示的に使用することは余計です。 Powershellはそれをあなたのために自動的に行います。
+0

こんにちはTomalak、非常に詳細な返信と回答をいただきありがとうございます。 私はデータのXML読み込みに取り組んだのは初めてで、コードはブログ記事のスクラップに基づいていました。 トップダウンロジックの問題は、私を悩ましていたものでした。私は、XMLがシーケンシャルに読み込まれ、ローとしてローディングするとシーケンスを処理すると思っていました。 XPathのヒントもありがとうございます。軸や述語について聞いたことがないので、私は今読んでいます。 あなたが掲示したコードは今や完全に意味をなさない。私が持っていた知識のギャップに取り組むのを助けてくれたことにとても喜んで感謝しています。 –

+0

大歓迎です。フィードバックをお寄せいただきありがとうございます。ドキュメントを順番に読み込むXML APIがありますが、デフォルトでPowershellで使用されるAPIはそうしません。すべてのものを1つの大きなツリーにRAMに読み込み、そのツリーをXPathでブランチに沿って移動できます。この方法は、RAMに簡単に収まるXMLファイルに適しています.GBサイズのXMLファイルを取得すると、順次APIが有用になり始めます。私は軸のこのグラフィカル表現が役立つことがわかった:https://our.umbraco.org/documentation/reference/templating/macros/xslt/xpath-axes-and-their-shortcuts – Tomalak

関連する問題