How do I iterate xml files that have many of the same tag per record? [duplicate] - xml-parsing

This question already has an answer here:
VBScript iterating through XML child nodes and retrieving values
(1 answer)
Closed 2 years ago.
I'm working on an old classic asp system that receives an xml file from another system that has recently changed the format of the xml file. It contains a video library summary I need to parse.
Sample xml as follows:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<videodb>
<version>1</version>
<movie>
<title>3 Days to Kill</title>
<ratings>
<rating name="themoviedb" max="10" default="true">
<value>6.000000</value>
<votes>1416</votes>
</rating>
</ratings>
<plot>A dangerous international spy... blah blah blah</plot>
<runtime>113</runtime>
<mpaa>Rated PG-13</mpaa>
<id>tt2172934</id>
<uniqueid type="imdb" default="true">tt2172934</uniqueid>
<uniqueid type="tmdb">192102</uniqueid>
<genre>Action</genre>
<genre>Drama</genre>
<genre>Thriller</genre>
<genre>Crime</genre>
<year>2014</year>
<status></status>
<code></code>
<trailer></trailer>
<actor>
<name>Kevin Costner</name>
<role>Ethan Renner</role>
</actor>
<actor>
<name>Amber Heard</name>
<role>Vivi Delay</role>
</actor>
<dateadded>2014-12-21 14:31:07</dateadded>
</movie>
<movie>
<title>47 Ronin</title>
<ratings>
<rating name="themoviedb" max="10" default="true">
<value>6.000000</value>
<votes>2324</votes>
</rating>
</ratings>
<plot>Kai—an outcast—joins Oishi, the leader of 47 outcast samurai...blah blah blah</plot>
<runtime>119</runtime>
<mpaa>Rated PG-13</mpaa>
<playcount>1</playcount>
<lastplayed>2020-03-24</lastplayed>
<id>tt1335975</id>
<uniqueid type="imdb" default="true">tt1335975</uniqueid>
<uniqueid type="tmdb">64686</uniqueid>
<genre>Drama</genre>
<genre>Action</genre>
<genre>Adventure</genre>
<genre>Fantasy</genre>
<year>2013</year>
<trailer></trailer>
<actor>
<name>Keanu Reeves</name>
<role>Kai</role>
</actor>
<actor>
<name>Hiroyuki Sanada</name>
<role>Kuranosuke Ôishi</role>
</actor>
<dateadded>2014-12-21 22:15:42</dateadded>
</movie>
</videodb>
My asp code is as follows....
Set objXMLDoc = Server.CreateObject("Microsoft.XMLDOM")
objXMLDoc.async = False
objXMLDoc.load Server.MapPath("/MovieLibrary/data/videodb.xml")
Dim xmlMovies
Set xmlMovies = objXMLDoc.documentElement.selectNodes("movie")
For Each xmlMovie In xmlMovies
'
Dim title:title = xmlMovie.selectSingleNode("title").text
Dim rating_value:rating_value = left(xmlMovie.selectSingleNode("ratings/rating/value").text,3)
Dim rating_votes:rating_votes = xmlMovie.selectSingleNode("ratings/rating/votes").text
Dim plot:plot = xmlMovie.selectSingleNode("plot").text
Dim runtime:runtime = xmlMovie.selectSingleNode("runtime").text
Dim mpaa_rating:mpaa_rating = xmlMovie.selectSingleNode("mpaa").text
Dim release_year:release_year = xmlMovie.selectSingleNode("year").text
Dim id:id = xmlMovie.selectSingleNode("id").text
Dim genre:genre = xmlMovie.selectSingleNode("genre").text
Dim date_added:date_added = xmlMovie.selectSingleNode("dateadded").text
Dim actor:actor = xmlMovie.selectSingleNode("actor/name").text
Response.Write Server.HTMLEncode(title) & " "
Response.Write Server.HTMLEncode(rating_value) & "/10 "
Response.Write Server.HTMLEncode(rating_votes) & "<br>"
Response.Write Server.HTMLEncode(plot) & "<br>"
Response.Write Server.HTMLEncode(runtime) & " Minutes "
Response.Write Server.HTMLEncode(mpaa_rating) & " "
Response.Write Server.HTMLEncode(release_year) & " "
Response.Write Server.HTMLEncode(id) & "<br> "
Response.Write Server.HTMLEncode(genre) & "<br>"
Response.Write Server.HTMLEncode(date_added) & "<br>"
Response.Write Server.HTMLEncode(actor) & "<br><br>"
Next
As you can see in the xml file each movie can have several "genre" entries and several "actor" entries. I can get all the entries but I can only get the first "genre" entry and the first "actor" entry. I'm having trouble figuring out how to create a sub-list of the genres listed for each movie so I can push them into my class....and I only want to grab the first 2 or three actors, not 27 as some do.
Obviously I need to create an array for the genre and actor fields but everything I try comes back with something like "this collection doesn't have this method" or nothing at all.
I know my code is messy, this is a test bed I created to make sure I can pull the new format of the file.
Any help will be appreciated. (sorry for the long post)

Use a function to get the text using selectNodes Method
Function getNodeValue(nodename)
Dim NodeValue : NodeValue = ""
Dim Nodes
set Nodes = xmlMovie.selectNodes(nodename)
For each Node in Nodes
If NodeValue = "" Then
NodeValue = Node.Text
Else
NodeValue = NodeValue & ", " & Node.Text
End If
Next
getNodeValue = NodeValue
End Function
This will return a comma delimited string.
Now you can get all the values like:
Dim title:title = getNodeValue("title")
and
Dim genre:genre = getNodeValue("genre")

Related

Migration Zephyr test-cases steps from Jira server to Jira cloud

Now we are on the process of migration from Jira server to the Jira cloud.
We are using Zephyr for Test Cases.
For now we have 1843 and they must be migrated as other tickets to Jira cloud.
We do not need to migrate all Test Cycles and all history of test execution, we need only Test Cases to use it in future Test Cycles.
This article contains answer on the same question
https://support.getzephyr.com/hc/en-us/community/posts/205799785-How-to-migrate-from-JIRA-Server-to-JIRA-Cloud
but utility doesn't work properly for me, after pressing Start Import button nothing happens.
How to migrate Test Cases from server Jira to cloud with Test Steps in Zephyr?
Finally I found the solution how to import all 1843 Test Cases automatically via tool from the article mention in the question.
Our test-cases were migrated to the cloud Jira as usual tickets. They have no Test Steps but have all other information like Description, Labels and other which relate to Jira fields. Further I will show how to migrate all steps to the migrated Test Cases without steps.
Go to your Jira and Export Test Cases that you need to Excel file. It can be done from this screen
https://zephyrdocs.atlassian.net/wiki/spaces/ZTD/pages/12386325/Search+Test+Executions
Download .jar file from the
https://bitbucket.org/zfjdeveloper/zfj-importer/downloads/
In cmd run this jar file via command java -jar zfj-importer-utility-0.40.jar
I tried to run jar file by double click, application opens but after configuration and press button Start Import nothing happens.
Only after opening from cmd everything works.
Plus in cmd you can see progress and error details which will help you in debug.
Configure utility as in documentation https://bitbucket.org/zfjdeveloper/zfj-importer/wiki/Home
At this point I though that after pressing Start Import everything will be perfect, but no.
In console I found a lot of error, their reason was a lot of line breaks inside test steps.
Lets say you have one step with one row in Step field, one row In Test Data field but in Execution result field you have text with line breaks, lets say 4 rows. For this case in excel Execution result field will be 4 different columns, Step field and Test Data as one merged column.
And based on utility rules there impossible to have result without step. (such issue can be if you have line break in Step field and Test Data).
Below I will show how I handle it.
I decided to write Excel function which will get rows from not merged rows for one step, concatenate them and provide to import.
Excuse me for my VBA, I have never use it before. Everything that I wrote can be rewritten in better way and in one script, but it works for me and I do not want to spend time more on this issue, so let go.
Below you can find 4 excel function. 3 of them are quite similar and difference is only in one letter. Last scrip is for deleting empty rows which were concatenated, without it steps with value "null" will be created.
Public Const lastTableRow = 3872
Function ConvertSteps()
Dim callerRow As Long
Dim isValueInStepId As Boolean
Dim isNoValueInNextStepId As Boolean
Dim result As String
Dim baseColumnLetter As String
Dim stepIdColumnLetter As String
callerRow = Application.Caller.row
baseColumnLetter = "S"
stepIdColumnLetter = "Q"
Debug.Print "processed row is: " & callerRow
isValueInStepId = (Range(stepIdColumnLetter & callerRow).Value <> "")
isNoValueInNextStepId = (Range(stepIdColumnLetter & (callerRow + 1)).Value = "")
If isValueInStepId And isNoValueInNextStepId Then
Dim i As Integer
i = 1
result = Range(baseColumnLetter & callerRow).Value
Do While Range(stepIdColumnLetter & (callerRow + i)).Value = "" And (callerRow + i) <= lastTableRow
result = result & " " & Range(baseColumnLetter & (callerRow + i)).Value
i = i + 1
Loop
ConvertSteps = result
Else
If Range(baseColumnLetter & (callerRow)).Value = "" Then
ConvertSteps = ""
Else
ConvertSteps = Range(baseColumnLetter & (callerRow)).Value
End If
End If
End Function
Function ConvertTestData()
Dim callerRow As Long
Dim isValueInStepId As Boolean
Dim isNoValueInNextStepId As Boolean
Dim result As String
Dim baseColumnLetter As String
Dim stepIdColumnLetter As String
callerRow = Application.Caller.row
baseColumnLetter = "T"
stepIdColumnLetter = "Q"
Debug.Print "processed row is: " & callerRow
isValueInStepId = (Range(stepIdColumnLetter & callerRow).Value <> "")
isNoValueInNextStepId = (Range(stepIdColumnLetter & (callerRow + 1)).Value = "")
If isValueInStepId And isNoValueInNextStepId Then
Dim i As Integer
i = 1
result = Range(baseColumnLetter & callerRow).Value
Do While Range(stepIdColumnLetter & (callerRow + i)).Value = "" And (callerRow + i) <= lastTableRow
result = result & " " & Range(baseColumnLetter & (callerRow + i)).Value
i = i + 1
Loop
ConvertTestData = result
Else
If Range(baseColumnLetter & (callerRow)).Value = "" Then
ConvertTestData = ""
Else
ConvertTestData = Range(baseColumnLetter & (callerRow)).Value
End If
End If
End Function
Function ConvertResult()
Dim callerRow As Long
Dim isValueInStepId As Boolean
Dim isNoValueInNextStepId As Boolean
Dim result As String
Dim baseColumnLetter As String
Dim stepIdColumnLetter As String
callerRow = Application.Caller.row
baseColumnLetter = "U"
stepIdColumnLetter = "Q"
Debug.Print "processed row is: " & callerRow
isValueInStepId = (Range(stepIdColumnLetter & callerRow).Value <> "")
isNoValueInNextStepId = (Range(stepIdColumnLetter & (callerRow + 1)).Value = "")
If isValueInStepId And isNoValueInNextStepId Then
Dim i As Integer
i = 1
result = Range(baseColumnLetter & callerRow).Value
Do While Range(stepIdColumnLetter & (callerRow + i)).Value = "" And (callerRow + i) <= lastTableRow
result = result & " " & Range(baseColumnLetter & (callerRow + i)).Value
i = i + 1
Loop
ConvertResult = result
Else
If Range(baseColumnLetter & (callerRow)).Value = "" Then
ConvertResult = ""
Else
ConvertResult = Range(baseColumnLetter & (callerRow)).Value
End If
End If
End Function
Public Sub DeleteBlankRows()
Dim SourceRange As Range
Dim EntireRow As Range
Set SourceRange = Range("Q1", "Q" & lastTableRow)
If Not (SourceRange Is Nothing) Then
Application.ScreenUpdating = False
For i = SourceRange.Rows.Count To 1 Step -1
Set EntireRow = SourceRange.Cells(i, 1).EntireRow
Debug.Print SourceRange.Cells(i, 1).Value
If SourceRange.Cells(i, 1).Value = 0 Then
EntireRow.Delete
End If
Next
Application.ScreenUpdating = True
End If
End Sub
Let's open Excel file and save it in .xlsm format to apply custom functions.
Import functions to Excel
in the top of the code set in variable lastTableRow last row with Test Case step in your Excel.
Now we need 3 new columns to save transferred Step, Test Data and Result fields. For this purpose we can use last column Comments, copy and past it two times. Now we have 3 empty column W, X, Y for our purpose.
For all rows in column W apply formula =ConvertSteps() to agregate steps (it can take some time)
For all rows in column X apply formula =ConvertTestData() to agregate test data (it can take some time)
For all rows in column Y apply formula =ConvertResult() to agregate results (it can take some time)
Now we have to convert values in new columns from formula to their string value. To do it select all table and press Ctrl+C. Then press right button and choose past values.
Run DeleteBlankRows macros to delete all rows that we do not need to import.
Save file in .xml format.
Choose this file in Utility and press Start Import
In cmd you can see a few errors. In my case they were releted to situation when there is no step description but there is expected result. If they are quite seldom as in my case, it's easier to change it mannualy in Execel file. If there a lot of them you can handle this case in custom function.
So thats it, this solution helped me to import 1800+ Test Cases.
I have exported them partially, by 500 and for me it takes about 3 hour to import all Test Cases.

I am trying to link a subform using Combo box, i write the following code

Dim mydrawing As String
mydrawing = "select * from tbl_welddata where([Drawingno] = " & Me!CboDrawingno & ")"
Me.tbl_welddatasubform.Form.RecordSource = mydrawing Me.tbl_welddatasubform.Form.Requery
I am getting an error message as;
Data type mismatch error 3464
Every time when I debug it pointing to this line
Me.tbl_welddatasubform.Form.RecordSource = mydrawing
could some one help me to sort it.
Put in a debug line, and study the output:
Dim mydrawing As String
mydrawing = "select * from tbl_welddata where([Drawingno] = " & Me!CboDrawingno & ")"
' If text, quotes are needed:
' mydrawing = "select * from tbl_welddata where([Drawingno] = '" & Me!CboDrawingno & "')"
Debug.Print mydrawing
Me!tbl_welddatasubform.Form.RecordSource = mydrawing
A requery is not needed.
Can you apply the resulting mydrawing manually?
Is Drawingno a number, not text?
Is tbl_welddatasubform the name of the subform control?

VBA to read in tab delimited file

I have some code which reads in a tab delimited file where cell reference B2 matches the reference in the first column in the tab delimited file. This works fine where the text file is small. The below works on a sample file with aa bb and cc as the headers with dummy data.
Option Explicit
Sub TestImport()
Call ImportTextFile(Sheet1.Range("B1"), vbTab, Sheet2.Range("A4"))
End Sub
Public Sub ImportTextFile(strFileName As String, strSeparator As String, rngTgt As Range)
Dim lngTgtRow As Long
Dim lngTgtCol As Long
Dim varTemp As Variant
Dim strWholeLine As String
Dim intPos As Integer
Dim intNextPos As Integer
Dim intTgtColIndex As Integer
Dim wks As Worksheet
Set wks = rngTgt.Parent
intTgtColIndex = rngTgt.Column
lngTgtRow = rngTgt.Row
Open strFileName For Input Access Read As #1
While Not EOF(1)
Line Input #1, strWholeLine
varTemp = Split(strWholeLine, strSeparator)
If CStr(varTemp(0)) = CStr(Range("B2")) Then
wks.Cells(lngTgtRow, intTgtColIndex).Resize(, UBound(varTemp) + 1).Value = varTemp
lngTgtRow = lngTgtRow + 1
End If
Wend
Close #1
Set wks = Nothing
End Sub
I am trying to get the below bit of code to work using ADO as this will run much faster on a text file with a couple of million records however I am getting an error on the '.Open str' part of the code (no value given for one or more required parameters).
It looks like it is to do with how I am defining the string- could you review and see if there is something I am missing...?
Sub QueryTextFile()
t = Timer
Dim cnn As Object
Dim str As String
Set cnn = CreateObject("ADODB.Connection")
cnn.Provider = "Microsoft.Jet.OLEDB.4.0"
cnn.ConnectionString = "Data Source=C:\Users\Davids Laptop\Documents\Other Ad Hoc\Test Files\;Extended Properties=""text;HDR=Yes;FMT=Delimited;"""
cnn.Open
Dim rs As Object
Set rs = CreateObject("ADODB.Recordset")
str = "SELECT * FROM [test1.txt] WHERE [aa]=" & Chr(34) & Range("B2") & Chr(34)
With rs
.ActiveConnection = cnn
.Open str
Sheet1.Range("A4").CopyFromRecordset rs
.Close
End With
cnn.Close
MsgBox Timer - t
End Sub

How to read quoted field from CSV using VBScript

In a sample.csv file, which has fixed number of columns, I have to extract a particular field value and store it in a variable using VBScript.
sample.csv
100,SN,100.SN,"100|SN| 435623| serkasg| 15.32|
100|SN| 435624| serkasg| 15.353|
100|SN| 437825| serkasg| 15.353|"," 0 2345"
101,SN,100.SN,"100|SN| 435623| serkasg| 15.32|
100|SN| 435624| serkasg| 15.353|
100|SN| 437825| serkasg| 15.353|"," 0 2346"
I want to parse the last two fields which are within double quotes and store them in two different array variables for each row.
You could try using an ADO connection
Option Explicit
dim ado: set ado = CreateObject("ADODB.Connection")
ado.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\txtFilesFolder\;Extended Properties=""text;HDR=No;FMT=Delimited"";"
ado.open
dim recordSet: set recordSet = ado.Execute("SELECT * FROM [samples.csv]")
dim field3, field4
do until recordSet.EOF
field3 = recordSet.Fields(3).Value
field4 = recordSet.Fields(4).Value
' use your fields here
recordSet.MoveNext
loop
recordSet.close
ado.close
You may have an issue if those fields are greater than 255 characters in length - if they are, they may return truncated. You also may have better luck with ODBC or ACE connection strings instead of the Jet one I've used here.
Since CSV's are comma-separated, you can use the Split() function to separate the fields into an array:
' Read a line from the CSV...
strLine = myCSV.ReadLine()
' Split by comma into an array...
a = Split(strLine, ",")
Since you have a static number of columns (5), the last field will always be a(4) and the second-to-last field will be a(3).
Your CSV data seems to contain 2 embedded hard returns (CR, LF) per line. Then the first line ReadLine returns is:
100,SN,100.SN,"100|SN| 435623| serkasg| 15.32|
The solution below unwraps these lines before extracting the required fields.
Option Explicit
Const ForReading = 1
Const ForAppending = 8
Const TristateUseDefault = 2 ' Opens the file using the system default.
Const TristateTrue = 1 ' Opens the file as Unicode.
Const TristateFalse = 0 ' Opens the file as ASCII.
Dim FSO, TextStream, Line, LineNo, Fields, Field4, Field5
ExtractFields "sample.csv"
Sub ExtractFields(FileName)
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FileExists(FileName) Then
Line = ""
LineNo = 0
Set TextStream = FSO.OpenTextFile(FileName, ForReading, False, TristateFalse)
Do While Not TextStream.AtEndOfStream
Line = Line & TextStream.ReadLine()
LineNo = LineNo + 1
If LineNo mod 3 = 0 Then
Fields = Split(Line, ",")
Field4 = Fields(3)
Field5 = Fields(4)
MsgBox "Line " & LineNo / 3 & ": " & vbNewLine & vbNewLine _
& "Field4: " & Field4 & vbNewLine & vbNewLine _
& "Field5: " & Field5
Line = ""
End If
Loop
TextStream.Close()
Else
MsgBox "File " & FileName & " ... Not found"
End If
End Sub
Here is an alternative solution that allows for single or multiline CSV records. It uses a regular expression which simplifies the logic for handling multiline records. This solution does not remove CRLF characters embedded in a record; I've left that as an exercise for you :)
Option Explicit
Const ForReading = 1
Const ForAppending = 8
Const TristateUseDefault = 2 ' Opens the file using the system default.
Const TristateTrue = 1 ' Opens the file as Unicode.
Const TristateFalse = 0 ' Opens the file as ASCII.
Dim FSO, TextStream, Text, MyRegExp, MyMatches, MyMatch, Field4, Field5
ExtractFields "sample.csv"
Sub ExtractFields(FileName)
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FileExists(FileName) Then
Set MyRegExp = New RegExp
MyRegExp.Multiline = True
MyRegExp.Global = True
MyRegExp.Pattern = """([^""]+)"",""([^""]+)"""
Set TextStream = FSO.OpenTextFile(FileName, ForReading, False, TristateFalse)
Text = TextStream.ReadAll
Set MyMatches = MyRegExp.Execute(Text)
For Each MyMatch in MyMatches
Field4 = SubMatches(0)
Field5 = SubMatches(1)
MsgBox "Field4: " & vbNewLine & Field4 & vbNewLine & vbNewLine _
& "Field5: " & vbNewLine & Field5, 0, "Found Match"
Next
Set MyMatches = Nothing
TextStream.Close()
Else
MsgBox "File " & FileName & " ... Not found"
End If
End Sub

Convert part of string to URL when displayed

I browsed around for a solution and I am sure it's a simple question but still not sure how to do that. So, I have a string that contains many words and some times it has links in it. For example:
I like the website http://somesitehere.com/somepage.html and I suggest you try it too.
I want to display the string in my view and have all links automatically converted to URLs.
#Model.MyText
Even StackOverflow gets it.
#Hunter is right.
In addition i found complete implementation in C#: http://weblogs.asp.net/farazshahkhan/archive/2008/08/09/regex-to-find-url-within-text-and-make-them-as-link.aspx.
In case original link goes down
VB.Net implementation
Protected Function MakeLink(ByVal txt As String) As String
Dim regx As New Regex("http://([\w+?\.\w+])+([a-zA-Z0-9\~\!\#\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?", RegexOptions.IgnoreCase)
Dim mactches As MatchCollection = regx.Matches(txt)
For Each match As Match In mactches
txt = txt.Replace(match.Value, "<a href='" & match.Value & "'>" & match.Value & "</a>")
Next
Return txt
End Function
C#.Net implementation
protected string MakeLink(string txt)
{
Regex regx = new Regex("http://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\#\\#\\$\\%\\^\\&\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);
MatchCollection mactches = regx.Matches(txt);
foreach (Match match in mactches) {
txt = txt.Replace(match.Value, "<a href='" + match.Value + "'>" + match.Value + "</a>");
}
return txt;
}
One way to do that would be to do a Regular Expression match on a chunk of text and replace that url string with an anchor tag.
Another regex that can be used with KvanTTT answer, and has the added benefit of accepting https urls
https?://([\w+?.\w+])+([a-zA-Z0-9\~!\##\$\%\^\&*()_-\=+\/\?.:\;\'\,]*)?
.net string representation:
"https?://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\#\\#\\$\\%\\^\\&\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?"

Resources