1

ここでの初めてのポスターであり、VBscript の初心者です。これを第二の性質のように知っている皆さんからの助けを本当に借りることができます. 私はいくつかの関連情報を含めようとしましたが、多すぎないことを願っています.

私はこれを機能させようとしてきましたが、数日間の試行と数十回のコードの反復の後、最終的に手を差し伸べています。XML ドキュメント内の複数のレベル (noes と chidlren) からデータを抽出する例は見つかりませんでした。

私は、VBScript を使用して XML ファイルからデータを抽出する任務を負っています。具体的な項目は次のとおりです。年、口座番号、現在の未払い額、延滞がありますか? (true/false) および書式付き令状番号。

XML ファイルの形式は次のとおりです。1,000 から 10,000 以上のノードがこのデータで満たされ、そこには多数の「その他」ノードも含まれています。

  <BillData>
    <BillHeader>
      <Year>2010</Year>
      <misc></misc>
      <misc2></misc2>
      <misc3></misc3>
      <AcctNumber>0002566129</AcctNumber>
      <misc4></misc4>
      <PayAmounts>
         <CurrentAmountDue>133.06</CurrentAmountDue>
         <misc5></misc5>
      </PayAmounts>
      <misc6></misc6>
      <HasDelinquents>true</HasDelinquents>
      <WarrantInfo>
         <FormattedWarrantNumber>201115447</FormattedWarrantNumber>
      </WarrantInfo>
     </BillHeader>
   </BillData>

CurrentAmountDue と FormattedWarrantNumber は常に存在するとは限りません。これは空白という意味ではありませんが、以下に示すように、CurrentAmountDue のエントリ全体が欠落している可能性があります。

<PayAmounts>
   <misc5></misc5>
</PayAmounts>

このデータをカンマ区切りのテキスト ファイルに抽出する必要があります。データが存在しない場合は、コマンドを挿入するだけでよいため、出力が最終的に Excel にインポートされると、空白であることがわかります。

私にとっての課題は、さまざまな子ノードに入り、データを正しく抽出することです。異なるノードを正しく選択できないようです。

これらは私が参照として使用したいくつかのリンクですが、機能していないようです。

http://technet.microsoft.com/en-us/magazine/2007.02.heyscriptingguy.aspx これは進むべき方向のように見えましたが、「Node Test Expected Here」というエラーが表示されます。

  Set colNodes=xmlDoc.SelectNodes("/BillData/BillHeader/*" (Year | Account | CurrentAmountDue)")

以下のこの手法の使用を提案する Stack に関する投稿を見つけましたが、2 つの値を超えるとうまくいきません。これは、CurrentAmountDue と FormattedWarrantNumber が、いわば XML のより深いレベルにあるためだと推測しています。

  strQuery = "/BillData/BillHeader/ " & _
  "[name()='Year' or name()='AccountNumber' or name()='HasDelinquents' or name()='CurrentAmountDue' or name()='FormattedWarrantNumber']"

驚いたことに、これを取得して一部の値を返すことができますが、すべてが同じループにあるわけではないため、出力はオフになり (最初の行には年のみが表示され、最後の行は欠落しています)、単なるコンマです。

   strQuery = "/BillData/BillHeader/*"
   Set colNodes=xmlDoc.selectNodes(strQuery)
   For Each objNode in colNodes 

   ' some lame if then statements that get the values, but this can't be the correct approach!
   ' these three items (Year, Account and HasDelinquents are under each BillHeader as far as I can tell, but this doesn't seem to be the most effective method.
     if objNode.nodeName = "Year" then strYear = objNode.text  
     if objNode.nodeName = "Account" then strAccount = objNode.text 
     if objNode.nodeName = "HasDelinquents" then strHasDelq = objNode.text 

          for each CurrentAmt in objNode.SelectNodes("./CurrentAmountDue")
                strCurrAmt = CurrentAmt.text
                ' i finally got a value here when I use msgbox to view it.'
          next

          for each WarrantNum in objNode.SelectNodes("./FormattedWarrantNumber")
                strWarNum = WarrantNum.text   
                ' getting this value also when I use msgbox to view it.
          next
   next

だから、私の試みが無駄であることがわかります。

また、この行を下に挿入しようとしました。最後のNEXTの直前に入れましたが、意図したとおりに動作しませんでした。また、いくつかの IF-Then ステートメントを挿入して、ファイルに書き込む前に Year と Account の値をチェックし、ファイルに書き込んだ後に値を消去しようとしました。それはほとんどうまくいきましたが、私の最初の行と最後の行は正しいデータを生成していません.

     objFileToWrite.WriteLine(strYear & "," & strAccount & "," & strCurrAmt & "," & strHasDelq & "," & strWarNum)

先史時代の私のコーディングの試みを笑い飛ばしてしまったので、手を貸してくれませんか? :)他に何か必要な場合はお知らせください。投資してくれてありがとう。私はあなたの何人かがこれを簡単に蹴ることができることを知っています.

4

2 に答える 2

1

問題の前半 (.CSV/.TXT ファイルの作成と書き込み) のローテクな「デザイン パターン」は次のとおりです。

Get an FSO
Open traget file for writing
WriteLine Header (optional)
Loop over your data to export
    Create empty Array (elements ~ columns)
    Fill elements (if possible)
    WriteLine Join(Array, Delimiter) to traget file
Close file

コード内:

  Option Explicit
  Dim oFS     : Set oFS = CreateObject("Scripting.FileSystemObject")
  Dim sFSpec  : sFSpec  = "..\data\step00.csv"
  Dim sDelim  : sDelim  = ";"
  Dim aFields : aFields = Split("Yr ANum Amnt Delq FWNum")
  Dim oTS     : Set oTS = oFS.CreateTextFile(sFSpec)
  Dim nRecs   : nRecs   = 10
  Dim nRec
  oTS.WriteLine Join(aFields, sDelim)
  For nRec = 1 To nRecs
      ReDim aData(UBound(aFields))
      aData(0) = nRec
      If nRec Mod 2 Then aData(1) = "odd"

      oTS.WriteLine Join(aData, sDelim)
  Next
  oTS.Close

  WScript.Echo oFS.OpenTextFile(sFSpec).ReadAll()

出力:

Yr;ANum;Amnt;Delq;FWNum
1;odd;;;
2;;;;
3;odd;;;
4;;;;
5;odd;;;
6;;;;
7;odd;;;
8;;;;
9;odd;;;
10;;;;

の違いをマークしてください

oTS.WriteLine Join(aData, sDelim)

objFileToWrite.WriteLine(strYear & "," & strAccount & "," & strCurrAmt & "," & strHasDelq & "," & strWarNum)
(spurious param list (), btw)

2 番目の部分のスケルトン (構造化 XML のループ) は、次のようになります。

Get an msxml2.domdocument
Configure
Load .XML file
If error
   deal with it
Else
   use top level XPath to get your top level nodelist
   Loop nodelist
      handle sub-parts
End If

コードで:

  Option Explicit
  Dim oFS     : Set oFS = CreateObject("Scripting.FileSystemObject")
  Dim sFSpec  : sFSpec  = oFS.GetAbsolutePathName("..\data\step01.xml")
  WScript.Echo oFS.OpenTextFile(sFSpec).ReadAll()

  Dim oXD : Set oXD = CreateObject("msxml2.domdocument")
  oXD.setProperty "SelectionLanguage", "XPath"
  oXD.async = False
  oXD.load sFSpec
  If oXD.parseError.errorCode Then
     WScript.Echo "fail", sFSpec
     WScript.Echo oXD.parseError.reason
  Else
     WScript.Echo "ok", sFSpec
     Dim ndlBills : Set ndlBills = oXD.selectNodes("/Bills/BillData/BillHeader")
     If ndlBills.length Then
        WScript.Echo ndlBills.length, "bill nodes"
        Dim ndBill
        For Each ndBill In ndlBills
            Dim ndSub
            Set ndSub = ndBill.selectSingleNode("Year")
            If ndSub Is Nothing Then
               WScript.Echo "no Year"
            Else
               WScript.Echo "Year", ndSub.text
            End If
            Set ndSub = ndBill.selectSingleNode("PayAmounts/CurrentAmountDue")
            If ndSub Is Nothing Then
               WScript.Echo "no Amount"
            Else
               WScript.Echo "Amount", ndSub.text
            End If
        Next
     End If
  End If

出力:

<?xml version="1.0" encoding="utf-8" ?>
<Bills>
 <BillData>
  <BillHeader>
   <Year>2012</Year>
  </BillHeader>
 </BillData>
 <BillData>
  <BillHeader>
   <PayAmounts>
    <CurrentAmountDue>123.45</CurrentAmountDue>
   </PayAmounts>
  </BillHeader>
 </BillData>
</Bills>

ok E:\trials\SoTrials\answers\19571565\data\Step01.xml
2 bill nodes
Year 2012
no Amount
no Year
Amount 123.45

各 BillHeader からのデータを .CSV の 1 行に入れたいのに、要素が欠落しているため、// や他の種類の緩やかなクエリを使用して間違ったマッピングを行う危険を冒さないでください。すべての「/Bills/BillData/BillHeader」のリストを取得してドリルダウンするだけです。

両方のスクリプトのマージ:

  Option Explicit
  Dim oFS     : Set oFS = CreateObject("Scripting.FileSystemObject")
  Dim sXFSpec : sXFSpec = oFS.GetAbsolutePathName("..\data\step02.xml")
  WScript.Echo oFS.OpenTextFile(sXFSpec).ReadAll()
  Dim sCFSpec : sCFSpec = "..\data\step02.csv"
  Dim sDelim  : sDelim  = ","
  Dim aFields : aFields = Split("Yr ANum Amnt Delq FWNum")
  Dim oTS     : Set oTS = oFS.CreateTextFile(sCFSpec)
  oTS.WriteLine Join(aFields, sDelim)

  Dim oXD : Set oXD = CreateObject("msxml2.domdocument")
  oXD.setProperty "SelectionLanguage", "XPath"
  oXD.async = False
  oXD.load sXFSpec
  If oXD.parseError.errorCode Then
     WScript.Echo "fail", sXFSpec
     WScript.Echo oXD.parseError.reason
  Else
     WScript.Echo "ok", sXFSpec
     Dim ndlBills : Set ndlBills = oXD.selectNodes("/Bills/BillData/BillHeader")
     If ndlBills.length Then
        WScript.Echo ndlBills.length, "bill nodes"
        Dim ndBill
        For Each ndBill In ndlBills
            ReDim aData(UBound(aFields))
            Dim ndSub
            Set ndSub = ndBill.selectSingleNode("Year")
            If Not ndSub Is Nothing Then
               aData(0) = ndSub.text
            End If
            Set ndSub = ndBill.selectSingleNode("PayAmounts/CurrentAmountDue")
            If Not ndSub Is Nothing Then
               aData(2) = ndSub.text
            End If
            oTS.WriteLine Join(aData, sDelim)
        Next
     End If
  End If
  oTS.Close

  WScript.Echo oFS.OpenTextFile(sCFSpec).ReadAll()

出力:

<?xml version="1.0" encoding="utf-8" ?>
<Bills>
 <BillData>
  <BillHeader>
   <Year>2012</Year>
  </BillHeader>
 </BillData>

  <BillHeader>
   <Year>0000</Year>
   <PayAmounts>
    <CurrentAmountDue>0.0</CurrentAmountDue>
   </PayAmounts>
   <junk/>
  </BillHeader>

 <BillData>
  <BillHeader>
   <PayAmounts>
    <CurrentAmountDue>123.45</CurrentAmountDue>
   </PayAmounts>
  </BillHeader>
 </BillData>

 <BillData>
  <BillHeader>
   <Year>2013</Year>
   <PayAmounts>
    <CurrentAmountDue>47.11</CurrentAmountDue>
   </PayAmounts>
  </BillHeader>
 </BillData>
</Bills>

ok E:\trials\SoTrials\answers\19571565\data\Step02.xml
3 bill nodes
Yr,ANum,Amnt,Delq,FWNum
2012,,,,
,,123.45,,
2013,,47.11,,

現実世界の問題を解決するには、次のような IF 句をさらに織り込むことができます。

Set ndSub = ndBill.selectSingleNode("XPath")
If Not ndSub Is Nothing Then
   aData(N) = ndSub.text
End If

または-おそらく長期的にはより良い

クエリの配列を定義します (フィールド順)

Dim aQueries : aQueries = Array( _ "年" _ , "PayAmounts/CurrentAmountDue" _ )

最も内側のループを

Dim ndBill
For Each ndBill In ndlBills
    oTS.WriteLine Join(getData(ndBill, aQueries), sDelim)
Next

getData() を定義する

Function getData(ndBill, aQueries)
  Dim nUb : nUb = UBound(aQueries)
  ReDim aData(nUb)
  Dim q
  For q = 0 To nUb
      Dim ndSub
      Set ndSub = ndBill.selectSingleNode(aQueries(q))
      If Not ndSub Is Nothing Then
         aData(q) = ndSub.text
      End If
  Next
  getData = aData
End Function
于 2013-10-25T08:16:20.010 に答える
0

Yearノードおよびのみが取得されますHasDelinquents。これは、ノードCurrentAmountDueおよびFormattedWarrantNumberが の直接の子ノードで/BillData/BillHeaderはなく、名前が付けられたノードがないためですAccountNumber(正しいノード名は ですAcctNumber)。XML ツリーの任意の場所からノードを選択するには、次のような式を試してください。

//*[name()='Year' or name()='AcctNumber' or name()='HasDelinquents' or name()='CurrentAmountDue' or name()='FormattedWarrantNumber']
于 2013-10-24T22:06:50.440 に答える