Link to home
Create AccountLog in
Avatar of Leif753
Leif753Flag for Sweden

asked on

IXMLDOMNodeList not defined

Hi experts
I am trying to learn to read and write XML. I am using vb.net version 2005. I am looking at different samples. When pasting the code I get a "not defined" error for the type IXMLDOMNodeList. I guess I need an Imports statement and perhaps also a reference. Which? Please help
Avatar of abel
abel
Flag of Netherlands image

Please show the code that you've got so far. The type IXMLDOMNodeList is not a known type (by my system) and I read/write XML all the time using VB/C#.NET. Is that the literal name? Can you paste the exact error? What do you mean with "pasting the code"?
Avatar of Leif753

ASKER

"pasting the code"? I just mean typing the code, sorry. Actually it is a sample from Microsoft
http://msdn.microsoft.com/en-us/library/ms765549(VS.85).aspx




Dim xmlDoc As New Msxml2.DOMDocument30
Dim objNodeList As IXMLDOMNodeList
xmlDoc.async = False
xmlDoc.Load ("books.xml")
If (xmlDoc.parseError.errorCode <> 0) Then
   Dim myErr
   Set myErr = xmlDoc.parseError
   MsgBox("You have error " & myErr.reason)
Else
   Dim str As String
   str = ""
   Set objNodeList = xmlDoc.getElementsByTagName("author")
   For i = 0 To (objNodeList.length - 1)
      str = str & objNodeList.Item(i).xml & vbCrLf
   Next
   MsgBox str
End If

Open in new window

Aha, you're trying to use the old MSXML2, and of that, version 3.0. That's very deprecated, do you really want to go that path? You use VB.NET 2005, instead of the above, you can use the inherent XmlReader, XmlDocument (equivalent of DOMDocument30) and all related methods inside the System.Xml namespace.

You can use the same techniques, but instead of handling the parseError procedurally, you can trap them using the usual try/catch. Here's a start:

(On a more general note, you seem to use the style of VBScript/VB6. Nothing wrong there, but life has become slightly easier. You do not need "Set" anymore. And it is a good custom now to always use a type (like Dim xya As String, as you already do in most cases). And since everything is an object in VB.NET, it adds little to say "objNodeList"... :)The for-each is an easy way of looping through a list (as in the example code). See also some other slight modifications I made to your code)



Dim xmlDoc As New XmlDocument()
Dim nodelist as XmlNodeList
Dim strAuthors As String
 
xmlDoc.Load("books.xml")
nodeList = xmlDoc.GetElementsByTagName("author")
 
For Each node As XmlNode In nodeList 
    strAuthors &= node.Value & vbCrLf
Next
 
MsgBox (strAuthors)

Open in new window

Oh, important thing: on top of your class, place an:

Imports System.Xml

otherwise you'll get errors. And, depending on how your project was created, if you still get errors (on the line "Imports System.Xml") then you are missing an assembly reference. Right-click your project and add the reference to System.Xml.
Avatar of Leif753

ASKER

OK thanks for the information regarding old and newer stuff. It's not always easy to know what is old code when searching the Internet. The code executes but I don't get it to work. I get no outcome for node.Value (value is nothing). The code snippet shows a part of the xml file from the sample I am using. Any more ideas?
<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
   <book id="bk102">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
      <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.</description>
   </book>
</catalog>

Open in new window

Just form the top of my head: node.innerText. But I'll check it with your XML table later this evening, ok?
When it is about XML it is all about nodes. Nodes come in different kinds. You have an element node (like author or book) and you have an attribute node (like id). You also have comment nodes and namespace nodes. But in your case, more importantly, you have text nodes. A text node is the text contained within tags.

To understand a text node, consider this:

<price>4.50</price>

Here, price is the element node and its first child is a text node" "4.50". Quite easy, isn't it? Now consider this:
<price discount="20%">4.50</price>

Here, price is the element node, discount is the attribute node and the first child of price is a text node: "4.50". Important to note that "20%" is the value of the attribute node and is not a node on its own. Still easy? I hope so, because:
<price discount="20%"><currency>¬</currency>4.50</price>

contains now how many nodes? Count carefully, don't look at the answer... Yes, you are right, it is 5. The element nodes price and currency, the attribute node discount and the two text nodes "¬" and "4.50". In the cases of the text nodes, the text content is their value.

Still not that hard, is it? Now, as a final example, consider the following:

<price discount="20%">
      <currency>¬</currency>
      4.50
</price>


how many nodes are we on now? You think 5? Think again... We just introduced a new text node, between price and currency. Even though it is only a few spaces, this is still counted as a node. Older parsers (like the one you used above) did not do well with empty text nodes, sometimes having them, sometimes not. The new parsers of Microsoft do it correctly.

Why would I tell you all this? Simply for you to understand why the following is the correct solution to getting the text out of the XML document. You said you wanted to learn to work with XML. I thought I give you some of the basics of it.

You also get it with innerText or innerXml, but these are only helper methods. Trying to use the DOM should better not include these methods as they usually introduce errors... but that's another story.

-- Abel --

Dim xmlDoc As New XmlDocument()
Dim nodelist As XmlNodeList
Dim strAuthors As String = ""
 
xmlDoc.Load("Data/Q_24151268.xml")
nodelist = xmlDoc.GetElementsByTagName("meta")
 
For Each node As XmlNode In nodelist
    strAuthors &= node.FirstChild.Value & vbCrLf
Next

Open in new window

ah, the currency, ¬ (euro symbol), does not do well on E-E. It was supposed to be the Euro sign. Think of

<currency>¬</currency>

as this:

<currency>EUR</currency>

and the rest will read just as well.
Avatar of Leif753

ASKER

Great info, thanks. However it is confusing with FirstChild, InnerText, value and so on. Let's look at the specific task I have. I am creating an application that reads a XML file. It loads data into a treeview. From the treeview the user can click on a node (Item in the XML file) and the childnodes of the Item are shown in text boxes.

There are two categories (parent nodes to the clickable one in the tree view) they are named Category and Subject in the XML File.

Let's concentrate on the first part loading the XML. I think I can deal with the loading to treeview and showing the child nodes to Item myself later. The last snippet you wrote makes it possible for me read the element nodes Name, reference, Imports and so on, fine so. But how do I get the Category attribute node (the word "IO") and the subjects attribute node (the word "FileHandling"). It is those attributes I would like to be visible in the tree view

Also note that this XML file I have created myself. It does not have to look like this. I am open for any idea to another structure
<?xml version="1.0" encoding="utf-8" ?>
<category name="IO">
	<Subject name="Filehandling">
		<Item id="1"> 
		
			<Name>Enumerate Folders in a folder</Name>
			<Reference></Reference>
			<Imports>System.IO</Imports>
			<Snippet>for each folder in folder next Folder
			where kalle
			order by pelle</Snippet>
			<Restrictions>No recursive call fo folders</Restrictions>
			<comment>This is comment 1</comment>
			<New />
		</Item>
		<Item id="2">
			<Name>Enumerate Files in a folder</Name>
			<Reference></Reference>
			<Imports>System.IO</Imports>
			<Snippet>fors each file in folder next Folder</Snippet>
			<Restrictions>No recursive call for files</Restrictions>
			<comment>This is comment 2</comment>
			<New />
		</Item>
	</Subject>
	<Subject name="File properties">
		<Item id="1">
			<Name>Get file Created</Name>
			<Reference></Reference>
			<Imports>System.IO</Imports>
			<Snippet>File.fileCreated</Snippet>
			<Restrictions></Restrictions>
			<comment>also you get other properties</comment>
			<New />
		</Item>
	</Subject>
</category>

Open in new window

The same way you can ask the inner text of a node or the firstchild, you can get its attributes:

Dim catNodeList As XmlNodeList = xmlDoc.GetElementsByTagName("category")
Dim catName As String = catNodeList(0).Attributes("name").Value

In the case of the Subject, you will have to create a little for-each loop because there are more of them.

On your XML: you're on the right track. Stay consequent. In XML world it is quite common to treat all elements in the same way: so either all MixedCaps, or all nocaps, or all dashed-elements (do not use underscore). That prevents errors.

Currently you use GetElementsByTagName. It is probably no that hard to imagine that its usage is limited. If you want more control, you can use .SelectNode or .SelectSingleNode. Both take an XPath which looks much the same as a file path. I.e., to get only the Item with id=2:

Dim item As Node = xmlDoc.SelectSingleNode("//Item[@id=2]")

it will search the whole tree for an item with an attribute "id" with the value "2". Quite a bit easier than looping through it yourself.
Avatar of Leif753

ASKER

Hi!
Despite a few hours work, I don't get the hang of the whole picture. But let's continue with my specific task. I have a new root element in the XML-file, Vbsamples, since we are going to have several categories. Thanks to the code snippet I am now able to read all Categories. And as you wrote I have to have a for loop to get all the child subjects for a category. In the existing For loop I must set a new root i.e. category = "ID", don't I?  After that I guess I can loop through the subjects that belongs to the Category. How do I do this? The samples I have seen with querying an attribute only returns a XMLNode. This must return a XmlNodeList.
xmlDoc.Load("c:\XMLTest.xml")
catNodeList = xmlDoc.GetElementsByTagName("Category")
For i = 0 To catNodeList.Count - 1
	catName = catNodeList(i).Attributes("Name").Value
Next i

Open in new window

If you ask for an attribute, you get an attribute... Since any element can have only one attribute (or zero) with a certain name, this:

catNodeList(i).Attributes("Name")

will return one attribute of the element catNodeList(i).

If you want all the categories that have an attibute Name (<category name="bla">, but not <category>), you can do:

XmlNodeList catsWithName = xmlDoc.SelectNodes("//Category[@Name]")

If you want all the categories with a specific Name atttribute with a specific value, i.e., you want <category name="abel"> but you do not want <category name="jan">, you can do this:

XmlNodeList catsWithNameAbel = xmlDoc.SelectNodes("//Category[@Name='abel']")

tbc...
> In the existing For loop I must set a new root i.e. category = "ID", don't I?

you hit on an important concept of XML in general and DOM walking in particular. Whenever you have selected a certain node, you can go onwards from there, it automatically becomes your new root the moment you try to do something with it. Any node (element node, not attribute nodes!) has this ability.

The following will select all Subjects that are under a Category:

Important note: XML is 100% case sensitive. In your uploaded XML snippet, you use "category" and "name", in your code snippet you use "Category" and "Name". These will not match.

General note on your XML: I said something on naming, which was about elements. Common practice in XML shows a strong consensus towards using lowercase names with dashes for the attributes. Of course, it is a matter of taste and a name does not make or break your code... ;-)

xmlDoc.Load("c:\XMLTest.xml")
catNodeList = xmlDoc.GetElementsByTagName("Category")
For i = 0 To catNodeList.Count - 1
    catName = catNodeList(i).Attributes("Name").Value
    XmlNodeList genres = catNodeList(i).GetElementsByTagName("Subject")
    ' do something with genre nodes '
    For Each genre As Node in genres
       Dim genreName As String = genre.Attributes("Name").Value
    Next
Next i

Open in new window

Avatar of Leif753

ASKER

Hi!
Yes, I have noticed that it is case sensitiv, I guess as a windows technician you're not so used to it. I guess you use C#. Seems to be differences between vb and C#. Looking at the code below I get two errors:
1) GetElementsByTagName is not a member of System.Xml.XmlNode
2) Type Node is not defined

        Dim xmlDoc As New XmlDocument()
        Dim catNodeList As XmlNodeList
        Dim genres As XmlNodeList
        Dim catName As String
        Dim i As Integer
        Dim strFileName As String = "..\..\XMLTest.xml"
 
        xmlDoc.Load(strFileName)
        catNodeList = xmlDoc.GetElementsByTagName("Category")
        For i = 0 To catNodeList.Count - 1
            catName = catNodeList(i).Attributes("Name").Value
            genres = catNodeList(i).GetElementsByTagName("Subject")
            ' do something with genre nodes '
            For Each genre As Node In genres
                Dim genreName As String = genre.Attributes("Name").Value
            Next
        Next i

Open in new window

Avatar of Leif753

ASKER

I see that I can be more precise. The line that got me the error "GetElementsByTagName is not a member of System.Xml.XmlNode "is: catNodeList(i).GetElementsByTagName
The other error is: For Each genre As Node In genres
ASKER CERTIFIED SOLUTION
Avatar of abel
abel
Flag of Netherlands image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Avatar of Leif753

ASKER

You don't have to apologize. It is working the things I have been asking for. I must give you a big thanks for all the help I've got. I've to make a stop here a look thoroughly through the material you have supplied me with. I might get back in the future. Thanks again Master!