Read String Array Fom XML into VBA - asp.net

I have the Following XML which is a result from a certain WEB-Service.
<?xml version="1.0" encoding="UTF-8"?>
-<ArrayOfString xmlns="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>16/May/2016 - 20/May/2016</string>
<string>20/May/2016 - 23/May/2016</string>
<string>23/May/2016 - 27/May/2016</string>
<string>27/May/2016 - 30/May/2016</string>
</ArrayOfString>
I have the Following VBA Code to read the Above XML
strRet = PostWebservice(strUrlEBenefit, strSoapAction, strXml)
intPos1 = InStr(strRet, "<string>") + 8
intPos2 = InStr(strRet, "</string>")
If intPos1 > 11 And intPos2 > 0 Then
xmlresult = Mid(strRet, intPos1, intPos2 - intPos1)
End If
as a result I'm Getting "16/May/2016 - 20/May/2016" in xmlresult.
What I want to do is getting all the date values Between all the [string] tags.
Can you please guide me how can I achieve the result? I understand I need to read it into array but I don't know how and didn't saw any useful tutorials for me(beginner in VBA and XML) to ref.

Read the xml-result which was returned from the web service into xml-document and use e.g. SelectSingleNode to select the node ArrayOfString. This node has namaspaces so you need to use namespaces in the xpath as well. Then just read all the child node texts e.g. into a collection here declared as result. HTH
Note: Add reference to Microsoft XML, v6.0 dll
Sub GetDateValues()
Dim xmlDocument As MSXML2.DOMDocument60
Dim xmlNamespaces As String
Dim arrayOfStringNode As IXMLDOMNode
Dim result As Collection
Dim xmlNode As IXMLDOMNode
Dim strRet As String
strRet = PostWebservice(strUrlEBenefit, strSoapAction, strXml)
Set xmlDocument = New DOMDocument60
If Not xmlDocument.LoadXML(strRet) Then
Err.Raise xmlDocument.parseError.ErrorCode, , xmlDocument.parseError.reason
End If
xmlNamespaces = "xmlns:myns='http://tempuri.org/'"
xmlDocument.setProperty "SelectionNamespaces", xmlNamespaces
Set arrayOfStringNode = xmlDocument.SelectSingleNode("/myns:ArrayOfString")
Set result = New Collection
For Each xmlNode In arrayOfStringNode.ChildNodes
result.Add xmlNode.Text
Next xmlNode
End Sub

I'm extrapolating using the answer from: 'dee', but reading in to an array with x number of columns. The reason for dNode below is because webservices often return auxiliary metadata at the same level as the actual value you want in your array. So you have to use SelectSingleNode at that point.
xmlDoc.SetProperty "SelectionNamespaces", xmlNamespaces
Set xmlNodes = xmlDoc.SelectNodes("/myns:.../...")
rws = xmlNodes.length
cols = var '# of columns from the request sent or extrapolate this code to count children of a single node at the level necessary
ReDim xay(1 To rws, 1 To cols)
For Each xNode In xmlNodes
r = r + 1
For c = 1 To UBound(xay, 2)
Set dNode = xNode.ChildNodes(c - 1)
Set dNode = dNode.SelectSingleNode("myns:THE_VALUE")
xay(r, c) = dNode.Text
Next c
Next xNode

Related

String as xml data source in XQJ

I would like to parse my XML string using an XQJ implementation, for example, SAXON. All examples I could find refer to some database connections. Is it possible to use simple String as xml source?
Saxon has an XQJ interface, and you could either use the doc function() from XQuery e.g. :
XQDataSource ds = new SaxonXQDataSource();
XQConnection conn = ds.getConnection();
XQPreparedExpression exp = conn.prepareExpression("doc('file:/some/file.xml')/child::node()");
XQResultSequence result = exp.executeQuery();
while(result.next()) {
System.out.println(result.getItemAsString(null));
}
or directly inject in the XML into the query. e.g. -
XQDataSource ds = new SaxonXQDataSource();
XQConnection conn = ds.getConnection();
XQPreparedExpression exp = conn.prepareExpression("<a><b>test</b></a>/child::node()");
XQResultSequence result = exp.executeQuery();
while(result.next()) {
System.out.println(result.getItemAsString(null));
}
Try using
void XQExpression.bindDocument(javax.xml.namespace.QName varName, javax.xml.transform.Source value, XQItemType type)
with XQConstants.CONTEXT_ITEM as the first argument, and a StreamSource wrapping a StringReadeer as the second.

Creating an array from values in XML

I want to get values from an XML file. How can I make array of the values in m1, m2 and m3?. How can do this?
Here is the XML
<?xml version="1.0"?>
<language>
<menus>
<m1>HomePage</m1>
<m2>Contact</m2>
<m3>About Us</m3>
</menus>
</language>
Here's is the ASP code I have:
Set Menus = xmlDoc.selectNodes("//language/menus/*" )
MenuCount = Menus.length
For Each entry in Menus
If entry.tagName = "m1" Then
m1 = entry.text
elseif entry.tagName="m2" then
m2 = entry.text
elseif entry.tagName="m3" then
m3 = entry.text
End If
Next
First I'm compeled to point out that the xml structure presented is poor. If each element inside <menus> represents a menu then all elements ought to have the same tag name such as <menu>. The use of 1, 2 and 3 suffix indicates the xml designer is confused as to the difference between an identifier and a value. If those values are important (i.e., the ordinal position with in the document can not be relied on) then those values should be included as an attribute:-
<menus>
<menu position="1">HomePage</menu>
<menu position="2">Contact</menu>
<menu position="3">About Us</menu>
</menus>
As to putting them in an array your code seems pretty close but here you go.
Dim menuNodes: Set menuNodes= xmlDoc.selectNodes("/language/menus/menu" )
Redim menus(menuNodes.length - 1)
Dim i : i = 0
For Each menuNode in menu
menus(i) = menu.Text
i = i + 1
Next
Are you saying that your current ASP code doesn't work? One method for obtaining values from XML file and using them within ASP is shown below;
<%
Dim menuItems 'Array of menu items
Set objHTTP = Server.CreateObject("Msxml2.ServerXMLHTTP")
objHTTP.open "GET","http://yourxmlfeed.xml",false
objHTTP.send
XMLData = objHTTP.responseText
' this code takes the raw RSSFeed and loads it into an XML Object
Set xmlFeed = Server.CreateObject("MSXML2.DomDocument.4.0")
xmlFeed.async = false
xmlFeed.LoadXml(XMLData)
Set objHTTP = Nothing
Set objItems = xmlFeed.getElementsByTagName("menus")
Set xmlFeed = Nothing
' loop over all the items in the XML Feed
For x = 0 to objItems.length - 1
Set objItem = objItems.item(x)
For Each objChild in objItem.childNodes
menuItems = menuItems & objChild.text & ","
Next
Next
response.write DeleteLastComma(menuItems) 'menuItems is your array of menu items
Function DeleteLastComma
...function to remove last comma off array
End Function
%>

LINQ TO XML Parse RSS Feed

I'm trying to parse an RSS feed using LINQ to Xml
This is the rss feed:
http://www.surfersvillage.com/rss/rss.xml
My code is as follows to try and parse
List<RSS> results = null;
XNamespace ns = "http://purl.org/rss/1.0/";
XNamespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
XDocument xdoc = XDocument.Load("http://www.surfersvillage.com/rss/rss.xml");
results = (from feed in xdoc.Descendants(rdf + "item")
orderby int.Parse(feed.Element("guid").Value) descending
let desc = feed.Element("description").Value
select new RSS
{
Title = feed.Element("title").Value,
Description = desc,
Link = feed.Element("link").Value
}).Take(10).ToList();
To test the code I've put a breakpoint in on the first line of the Linq query and tested it in the intermediate window with the following:
xdoc.Element(ns + "channel");
This works and returns an object as expect
i type in:
xdoc.Element(ns + "item");
the above worked and returned a single object but I'm looking for all the items
so i typed in..
xdoc.Elements(ns + "item");
This return nothing even though there are over 10 items, the decendants method doesnt work either and also returned null.
Could anyone give me a few pointers to where I'm going wrong? I've tried substituting the rdf in front as well for the namespace.
Thanks
You are referencing the wrong namespace. All the elements are using the default namespace rather than the rdf, so you code should be as follow:
List<RSS> results = null;
XNamespace ns = "http://purl.org/rss/1.0/";
XDocument xdoc = XDocument.Load("http://www.surfersvillage.com/rss/rss.xml");
results = (from feed in xdoc.Descendants(ns + "item")
orderby int.Parse(feed.Element(ns + "guid").Value) descending
let desc = feed.Element(ns + "description").Value
select new RSS
{
Title = feed.Element(ns + "title").Value,
Description = desc,
Link = feed.Element(ns + "link").Value
}).Take(10).ToList();

Create a VB.NET Array with two columns of values?

I know how to create an array and loop through it normally - but what if I need a multi-column array. e.g. usually I might do something like:
For Each row in NameofArray
Dim name as String = row
Response.Write("Hello " & name & "!")
Next
But what if I want to do something like:
For Each row in NameofArray
Dim name as String = row.name
Dim age as Integer = row.age
Response.Write("Hello " & name & "! You are " & age & " years old!"
Next
If this isn't possible with an array, is there another way I can accomplish this?
Create your custom data type:
public struct DataType
public string Name;
public int Age;
}
Such type you can than use in an array like that:
DataType[] myData = new DataType[100];
myData[0].Name = "myName";
myData[0].Age = 100;
Note, if looping through that array via foreach, the elements returned for each iteration cannot get altered. If this is an requirement for you, consider using 'class' rather than 'struct' in the above DataType declaration. This will come with some other implications though. For example, the instances of a class DataType will explicitely have to be created via the 'new' keyword.
After reading your comment I think my other answer is probably what you are looking for.
What type is row and what type is NameOfArray?
If you would like to make row into a coumpound type with several members then there a several options.
Structure Row
Public Name as String
Public Age as Integer
End Structure
for instance. If you would prefer a reference type substitute Class for Structure.
Or using anonymous types,
Dim row = New With {Name = "Bob", Age = 21}
Then you can use generics to make a list of rows that you can iterate through using ForEach.
Dim NameOfList As System.Collections.Generic.List(of Row)
or if it were a result of a LINQ query somthing that supported
IEnumerable(of New With{Name As String, Age As Int}). //Not sure if this is VB
I'm not certain I uderstand your question and hope this is the kind of thing you were looking for.
As you can see from my fellow answerers, the support for anonymous types is superior in C# but, since you asked the question in VB.Net I will limit myself to that context.
After reading your comment I think I understand the question.
You can do
///Spacer Top
Dim NameOfArray = {New With {.Age = 21, .Name = "Bob"}, New With {.Age = 74, .Name = "Gramps"}}
///Spacer Bottom
If you want to create an IEnumberable anonymous type of Name Age tuples ;-p
Did you tried Dictionary Class. You can loop through the Dictionary using KeyValue pair class.
// Create a new dictionary of strings, with string keys.
//
Dictionary<string, string> openWith =
new Dictionary<string, string>();
// Add some elements to the dictionary. There are no
// duplicate keys, but some of the values are duplicates.
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");
foreach(var item in openWith)
{
Console.WriteLine(item.Key +" can be open with " + item.value);
}
You need to (can) index into your array using the two dimensions ie...
Dim array(,) As Object = { _
{"John",26}, _
{"Mark",4} _
}
For row As Integer = 0 to array.GetUpperBound(0)
Dim name as String = CStr(array(row,0))
Dim age as Integer = CInt(array(row,1))
Response.Write("Hello " & name & "! You are " & age & " years old!")
Next
Though would be better storing this sort of information in a class or user defined type of some kind.

System.OutOfMemoryException

I have a program written in asp.net with lucene.net. At first I create an index from 28000 documents.
Secondly I'm executing a search, but sometimes there is an error. (I think this error is thrown when there are many results)
The important part of code:
Dim hits As Hits = searcher.Search(query)
Dim results As Integer = hits.Length() 'ergebnisse (größe der hits)
'#####################
'####### RESULTS #####
'#####################
trefferanzahl = results
If (results > 0) Then
Dim i As Integer
Dim h As Integer = results - 1
ReDim array_results(h, 6) 'array zum speichern von den "feldern"
Dim cellX As New TableCell()
For i = 0 To results - 1 Step 1
Dim tmpdoc As Document = hits.Doc(i) ' HERE THE ERROR!
Dim score As Double = hits.Score(i)
MsgBox("2. Docname: " & hits.Doc(i).Get("title"))
array_results(i, 0) = tmpdoc.Get("title")
array_results(i, 0) += tmpdoc.Get("doc_typ")
array_results(i, 1) = tmpdoc.Get("pfad")
array_results(i, 2) = tmpdoc.Get("date_of_create")
array_results(i, 3) = tmpdoc.Get("last_change")
array_results(i, 4) = tmpdoc.Get("id")
array_results(i, 5) = tmpdoc.Get("doc_typ")
array_results(i, 6) = CStr(score)
Next
' Load this data only once.
ItemsGrid.DataSource = CreateDataSource()
ItemsGrid.DataBind()
Else
bool_Suchergebnis = False
End If
searcher.Close()
Thanks in advance
A good principle when performing searches accross very large collections is to limit the results that you are processing as soon as possible. I will assume that you are implementing paging in your grid. And lets assume that PageSize is 20.
What you need to do is make sure that you have access to the PageSize and the current PageNo within this method. Then use Linq accross the result set to Take(PageSize) and Skip (PageNo * PageSize). Then you will only have to process 20 records.
Then, you have two options. If you are binding directly to the array, you might be able to get away with empty items, but I am not sure, so you might have to place dummy items into the datasource array in all positions that won't be displayed. Not ideal, but certainly quicker than processing 1000s of Hits.
The second option is to bind only the 20 items to the grid, which will be quick, switch off paging on the grid as it will only show one page and then implement your own paging behaviour as you know the PageSize, and the current PageNo. This will take more work but it will perform a lot faster than the out-of-the-box gridview binding to a large datasource.
And it will help you solve your memory problem.

Resources