Using VB6 (not my decision) and MSXML4, I'm trying to create and send an XML file to a web services call (exe) that transmits the XML as inputs to another application and returns another XML as outputs. I'm having trouble in the following areas:
* Creating attributes for certain elements. It seems like I can only do it on the root.
* Referencing the web services call application (*.exe) in VB6 & then sending it the XML input file as an argument.
* Validating an XML input file against a *.xsd schema
If there's an easy way to bring up a template XML file (schema?) and then populate it as needed, I'd like to know how. That would make my life a lot easier. Right now I'm trying to create the XML input files from scratch.
The only book resource I've found that handles VB6/XML is XML Programming with VB and ASP by Mark & Tracey Wilson (2000). I've had trouble taking sample code from web and getting it to work properly. Any guidance would be appreciated. Thank you!
I'm having trouble populating attributes in the code below. It works for the root but not the child level. I've tried various ways but can't get it to work. Please help me if you can. Thanks!
Private Sub cmdTestVBF_Click()
Dim oRoot As IXMLDOMElement
Dim oLevel1 As IXMLDOMElement
Dim oLevel2 As IXMLDOMElement
Dim oNode As IXMLDOMNode
XMLOUTPATH = App.Path & "\XmlOuput_TestVBF.xml"
Set oDoc = New DOMDocument
oDoc.resolveExternals = True
Set oRoot = oDoc.createElement("Parent")
Set oDoc.documentElement = oRoot
oRoot.setAttribute "xmlns", "http://blah/blah/blah"
oRoot.setAttribute "xmlns:i", "http://www.w3.org/2001/XMLSchema-instance"
Set oNode = oDoc.createElement("Child1")
oNode.Text = "2010-03-08T00:00:00"
oRoot.appendChild oNode
Set oLevel1 = oDoc.createElement("Child2")
oRoot.appendChild oLevel1
Set oLevel2 = oDoc.createElement("Grandchild")
'Want entry to look like: <Grandchild i:type="abc">
oLevel1.appendChild oLevel2
Set oNode = oDoc.createElement("GreatGrandchild")
oNode.Text = "Adding text works fine"
oLevel2.appendChild oNode
Set oNode = oDoc.createElement("Child3")
oNode.Text = "Adding text works fine"
oRoot.appendChild oNode
Set oNode = oDoc.createElement("Child4")
'Want entry to look like: <Child4 i:nil="true" />
oRoot.appendChild oNode
' Save xml file
oDoc.save XMLOUTPATH
MsgBox XMLOUTPATH & " is created for you."
End Sub
DESIRED OUTPUT (spacing not shown correctly in this post but code generates properly spaced XML file. I just need help on how to populate attributes.)
Public Function AddAttribute(oDOM As DOMDocument, _
oElement As IXMLDOMElement, _
sName As String, _
sValue As String, _
Optional bReplace As Boolean = False _
) As Boolean
On Error GoTo ErrHand
Dim oAttr As IXMLDOMAttribute
Dim bResults As Boolean
' First see if the attribute already exists ...
If (Not oElement.Attributes.getNamedItem(sName) Is Nothing) Then
If bReplace = False Then
bResults = False
End If
Else
Set oAttr = oDOM.createAttribute(sName)
oElement.setAttribute sName, sValue
bResults = True
End If
ErrHand:
Set oAttr = Nothing
If Err.Number <> 0 Then
Call SetErrorInfo(Err.Number, Err.Description, "CDomFunctions." & Err.Source, UNKNOWN)
bResults = False
End If
AddAttribute = bResults
End Function
Thanks, Marty! The code you supplied for adding attributes worked. I will look into the link for validation against schema. Sorry about posting overlapping postings. I posted the second one after realizing I didn't provide enough detail in the first one.
Here's some more detail regarding my original post above regarding "Referencing the web services call application (*.exe) in VB6 & then sending it the XML input file as an argument.":
Once I have the XML file similar to the one created above, then I need to send it to a web services client app (webservicecall.exe). This application takes my XML input file and generates an output XML file. I'm having trouble calling the application with the required arguments in VB6. The following code works from the DOS prompt, assuming the test_input_VB6.xlm is created:
Do you know how I should set up this call in VB6? Finally, what's the easiest way to reference one NodeTypedValue of the XML output file? Any further help would be greatly appreciated. Thanks again!
My application call from within VB6 just happens to be for web services, but I'm pretty sure the syntax would be the same regardless. So if you're familiar with making external calls to exe files from within VB6, your words of wisdom would help. Otherwise, I'll wait for someone else to respond and I'll continue tinkering with it. Also, I'll search more in the forum. Thanks!
There is almost nothing to be gained by going through a DOM to create a file containing XML. Just write the XML text to a file. If you need a particular encoding like UTF-8 you can use an ADODB.Stream object to handle the conversion from internal Unicode to another encoding, use LF as line-ends as needed, etc.
An XSD is almost useless for creating an XML document. The main purpose of XSD is:
To serve as particularly clunky documentation for programmers creating an XML document.
For automated validation of XML documents produced by another program.
You can't feed XSD to a DOM object and get an XML document. The XSD is at best a template for a highly variable structure. Many nodes defined in XSD can be optional or occur some variable number of times, and this is also true of sub-nodes.
You can't "call" an EXE, but you can run it. I see no advantage to calling ShellExecute to accomplish this. Just use VB's intrinsic Shell function. Shell will return a task ID for the child process, which can be converted to a process handle you can use to wait for the child to complete. Get the process handle by calling OpenProcess. If you require greater functionality you'll have to call CreateProcess yourself, which is what both Shell and ShellExecute do for you (after making a number of assumptions).
A search here should turn up multiple examples of "shell and wait" code.
Thanks, dilettante! I found a snippet of "shell and wait" code that works for my situation. It generates the desired XML output file. Now I only need to read one value from an element returned in that XML output file. Is there a simple way to extract this value? (I haven't programmed much in VB6 since the late 1990's, and I'm struggling to get back in the swing of things, especially with XML.) Any help would be appreciated!
I didn't see anything out there related to Splits. Basically, I'm having trouble finding code to do a very simple task, which is to load an XML file and read one or more values from certain nodes. For example, if I had the xml file below, what would I need to do to open and read the values of 100 and 200 from the GreatGrandchild nodes? Does it matter that the nodes are called the same thing if there's some other way to distinguish them (abc, def, etc.)?
Keep in mind this is very simple-minded. It makes a number of assumptions:
You only need the GreatGrandchild text (values).
Tags don't have erratic whitespace in them, and GreatGrandchild tags have no attributes.
Code:
Sub ParseCountDisplay()
Dim intXMLFile As Integer
Dim strXML As String
Dim strXMLParts() As String
Dim strGGCParts() As String
Dim intXMLPart As Integer
Dim intGGCCount As Integer
intXMLFile = FreeFile(0)
Open "GreatGrandchild.xml" For Input As #intXMLFile
strXML = Input$(LOF(intXMLFile), intXMLFile)
Close #intXMLFile
strXMLParts = Split(strXML, "<GreatGrandchild>", , vbTextCompare)
'Skip 0th entry, no data there:
For intXMLPart = 1 To UBound(strXMLParts)
strGGCParts = Split(strXMLParts(intXMLPart), _
"</GreatGrandchild>", _
, _
vbTextCompare)
If UBound(strGGCParts) = 1 Then
'Found a good GreatGrandchild.
txtLog.Text = txtLog.Text & strGGCParts(0) & vbNewLine
intGGCCount = intGGCCount + 1
End If
Next
txtLog.Text = txtLog.Text & vbNewLine _
& "Count = " & CStr(intGGCCount) & vbNewLine
Caption = Caption & " (Done)"
End Sub
Both of those assumptions may by acceptable to you, or not. If not, you can write more sophisticated parsing logic, probably based on InStr() instead of Split().
However if your requirements get much more sophisticated you can always use MSXML. You can do this either using SAX directly, or by using a DOM and letting it invoke SAX internally to parse the document into a DOM tree.
My offhand guess would be that your XML documents are well under 50KB and performance is not critical. This means the extra coding needed to use the SAX parser in MSXML yourself isn't worthwhile. Your average Joe typically just slops it into a DOM and traverses the resulting node tree, with no ill impact.
If your needs are really simple though, very simple parsing techniques can be used to extract nominal amounts of information without using MSXML at all.
This can get a little complicated. If you want UTF-8 you can't just use the DOM's .xml property because it always converts to UTF-16LE/UCS-2. It has to, because it returns a String and a String is always UTF-16.
Note the differences in results from the two MsgBox popups (modified example from MSDN Library):
Code:
Dim xmlDoc As New MSXML2.DOMDocument40
Const attribs As String = _
"version=""1.0"" encoding=""UTF-8"" standalone=""no"""
Dim pi As IXMLDOMProcessingInstruction
Dim stm As New ADODB.Stream
xmlDoc.async = False
xmlDoc.loadXML "<root><child/></root>"
If xmlDoc.parseError.errorCode <> 0 Then
MsgBox xmlDoc.parseError.reason, , "Parse error"
Else
Set pi = xmlDoc.createProcessingInstruction("xml", attribs)
xmlDoc.insertBefore pi, xmlDoc.childNodes.Item(0)
MsgBox xmlDoc.xml, , "String property .xml"
With stm
.Open
xmlDoc.save stm
.Position = 0
.Type = adTypeText
.Charset = "utf-8"
MsgBox .ReadText(adReadAll), , "Method .save output converted to UTF-16"
.Close
End With
End If
The second result is not UTF-8. It shows the UTF-8 output from the xmlDoc.save after converting it from UTF-8 to "Unicode" (UTF-16LE) using an ADODB.Stream object.