[英]using VBscript to extract data from XML from nodes and children
第一次來這里,是VBscript的新手。 我真的可以從你們這像第二天性的家伙那里得到一些幫助。 我試圖提供一些相關信息,希望不要太多。
我一直在嘗試使其工作,並在經過幾天的嘗試和十多次代碼迭代之后終於達到了極限。 我還沒有在XML文檔中找到從多個級別(noes和chidlren)提取數據的示例。
我的任務是使用VBScript從XML文件提取數據。 具體項目為:年份,帳號,當期應付款,是否拖欠款項? (是/否)和格式化的憑單編號。
XML文件的格式如下,從1,000到10,000多個節點中填充了此數據,同時還有大量的“ misc”節點。
<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/zh-cn/magazine/2007.02.heyscriptingguy.aspx,這似乎是前進的方向,但出現錯誤“期望在此處進行節點測試”:
Set colNodes=xmlDoc.SelectNodes("/BillData/BillHeader/*" (Year | Account | CurrentAmountDue)")
我在Stack上找到了一篇建議在下面使用此技術的帖子,但是一旦我超過了兩個值,它就對我不起作用,而我卻擁有更多。 我猜這是由於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)
好吧,既然您對我的史前編碼嘗試感到咯咯地笑,您能幫我一下嗎? :)讓我知道是否還有其他需要。 感謝您投入任何時間。 我知道有些人可能會輕松解決這個問題。
問題上半部分的技術含量低的“設計模式”(創建並寫入.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)
第二部分的框架-遍歷結構化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的一行中並且缺少元素時,請不要使用//或其他類型的松散查詢來冒險使用錯誤的映射。 只需獲取所有“ / 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(_“ Year” _,“ 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
您僅獲得Year
和HasDelinquents
節點,因為CurrentAmountDue
和FormattedWarrantNumber
節點不是/BillData/BillHeader
直接子節點,並且沒有名為AccountNumber
的節點(正確的節點名稱為AcctNumber
)。 要從XML樹中的任何位置選擇節點,請嘗試以下表達式:
//*[name()='Year' or name()='AcctNumber' or name()='HasDelinquents' or name()='CurrentAmountDue' or name()='FormattedWarrantNumber']
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.