4

I am pretty new to Visual Basic so I want to excuse myself right from the beginning.

The code below is supposed to get a node list with all nodes where the InnerText of Name is equal to the String named membername. This part seems to work perfectly, but afterwards I also want to check whether in this list a node Logout already exists. This way I want to prevent duplicating the data in the xml database. Unfortunately it doesn't work the way I tried it. It keeps duplicating all the data. So what's my mistake??

XML FILE

<?xml version="1.0" encoding="utf-16"?>
<Times>
  <Shift>
    <Name>Philipp</Name>
    <Login>14:11</Login>
    <Date>25.03.2013</Date>
    <Logout>14:11</Logout> ' Don't generate again ....
  </Shift>
  <Shift>
    <Name>Philipp</Name>
    <Login>14:11</Login>
    <Date>25.03.2013</Date>
    <Logout>14:11</Logout> ' Generate Logout node
  </Shift>
</Times>

VISUAL BASIC CODE

   If File.Exists(Filename) Then

        DOMDocument.Load(Filename)

        Dim RootElement As XmlElement = DOMDocument.DocumentElement
        Dim ListOfTitles As XmlNodeList = DOMDocument.GetElementsByTagName("Name")

        For Each Node As XmlNode In ListOfTitles

            If Node.InnerText = memberName Then

                Dim logout = Node.SelectNodes("Logout")

                If Not logout Is Nothing Then

                    Dim NewElement As XmlElement = DOMDocument.CreateElement("Logout")
                    NewElement.InnerText = DateTime.Now.ToShortTimeString()

                    Dim Parent As XmlNode = Node.ParentNode
                    Parent.AppendChild(NewElement)

                    DOMDocument.Save(Filename)

                End If

            End If

        Next
    End If
3
  • I changed your tag from "vb6" to "vb.net". Commented Mar 29, 2013 at 0:46
  • I +1'd GojiraDeMonstah's answer. If it is not too late, if you have fewer than 1000 lines of code hitting this XML Run, RUN, RUNNNN as fast as you can away from direct manipulation of XML and run TO using objects. I can't stress how wonderful it will be to be able to code 3x as fast with 1/3 the bugs. Al I can say is, trust me, you'll like it. Commented Apr 2, 2013 at 20:12
  • Am I wrong, or InnerText should be just Text Commented Apr 2, 2013 at 23:22

3 Answers 3

3

logout is being set to an empty object, so If Not logout is Nothing Then is equivalent to If True Then. See:http://msdn.microsoft.com/en-us/library/hcebdtae.aspx . The method IXMLElement.selectNodes(expression) always returns an object. To fix, check the length value of logout instead.

If logout.Count > 0 Then or use selectSingleNode which returns NULL if no node is found.

Sign up to request clarification or add additional context in comments.

7 Comments

could you please add some more detailed code?? logout.length doesn't work. I tried using it with selectsinglenode. And your link refers to a c# and c++ link.
@PhilippBraun The link refers to MS XML (admittedly the WinCE version) which is what you're using in VB6. Most of the MSDN documentation is written with C++ in mind.
Tanks, but I still don't get how to use .length here.
@Philipp Braun, what Motes is saying is to replace your "If Not logout is Nothing Then" with "If logout.length > 0 Then" or replace "Dim logout = Node.SelectNodes("Logout")" with "Dim logout = Node.SelectSingleNode("Logout")"
@PhilippBraun - It should be logout.Count > 0
|
2
+100

Let's try to explain what is wrong and how to solve it.

Apparently you wish, for each <Name>node, to retrieve the <Logout> node associated with it, if it exists, and you tried Node.SelectNodes("Logout") to do this, but:

The code Node.SelectNodes("Logout") actually attempts to select child nodes of the <Name> node represented by your Node variable - while in your XML markup,

  1. <Name> nodes do not have children and

  2. <Logout> nodes are always their siblings, not children.

So it always returns an empty collection, never the node you want.

You can solve this by iterating over your <Shift> nodes, since they contain both Name and Logout information as child nodes.

By the way, InnerText is not an IXMLDOMNode property, try using Text instead

If File.Exists(Filename) Then

    DOMDocument.Load(Filename)

    Dim RootElement As XmlElement = DOMDocument.DocumentElement
    Dim ListOfShifts As XmlNodeList = DOMDocument.GetElementsByTagName("Shift")

    For Each ShiftNode As XmlNode In ListOfShifts

        Dim NameNode = ShiftNode.SelectSingleNode("Name")
        If NameNode IsNot Nothing And NameNode.Text = memberName Then

            Dim logoutNode = ShiftNode.SelectSingleNode("Logout")

            If Not logoutNode Is Nothing Then

                Dim NewElement As XmlElement = DOMDocument.CreateElement("Logout")
                NewElement.Text = DateTime.Now.ToShortTimeString()

                Dim Parent As XmlNode = Node.ParentNode
                Parent.AppendChild(NewElement)

                DOMDocument.Save(Filename)

            End If

        End If

    Next
End If

Also, you are saving the document on every change - why not save it only once after the loop has finished

Comments

1

This isn't exactly what you're asking, but I find that dealing with raw XML leads to lots of headache. What you might consider instead is to just deal with a Shift class that lets you perform logic on your login/logout, and let .NET do the serialization/deserialization for you.

That way you are not bound to specific XML Paths if your business objects and relationships change.

Again, not what you asked, but here is how I would solve the business case you're dealing with.

First, create a shift class that I can put business logic in. Simple example here:

Public Class Shift

    Public Property Name As String
    Public Property DateString As String
    Public Property Login As String
    Public Property Logout As String

End Class

Next, create a collection of shifts. I called this class TimeCollection, but call it whatever you want. Mark it Serializable so that .NET can do the work of turning it from an object to XML and vice versa.

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.IO
Imports System.Xml.Serialization

<Serializable()> Public Class TimeCollection

Public Property Path As String

<XmlArray("Shifts")>
<XmlArrayItem(GetType(Shift))>
Public Property Shift As Shift()

Public Function Serialize(FileInfo As System.IO.FileInfo)
    Try

        If File.Exists(FileInfo.FullName) Then
            File.Delete(FileInfo.FullName)
        End If

        If Not Directory.Exists(FileInfo.DirectoryName) Then
            Directory.CreateDirectory(FileInfo.DirectoryName)
        End If

        Me.Path = FileInfo.FullName

        Dim serializer As XmlSerializer = New XmlSerializer(GetType(TimeCollection))
        Dim writer As StreamWriter = New StreamWriter(FileInfo.FullName)

        serializer.Serialize(writer, Me)
        writer.Close()

    Catch ex As Exception

        Throw
    End Try

End Function

Public Shared Function Deserialize(FileInfo As FileInfo) As TimeCollection


    Dim serializedType As TimeCollection = Nothing

    Dim path As String = FileInfo.FullName

    If (Not File.Exists(path)) Then
        Deserialize = serializedType
    Else
        Try
            Dim serializer As XmlSerializer = New XmlSerializer(GetType(TimeCollection))
            Dim reader As StreamReader = New StreamReader(path)
            serializedType = serializer.Deserialize(reader)
            reader.Close()
            Deserialize = serializedType
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End If

End Function

End Class

Now. If you have some code that generates a series of shifts, like this:

    Dim tc As TimeCollection = New TimeCollection() 

    Dim first As Shift = New Shift()
    first.Name = "Philipp"
    first.Login = "14:11"
    first.Logout = "14:45"
    first.DateString = "3/31/2013"

    Dim second As Shift = New Shift()
    second.Name = "Phillip"
    second.Login = "14:09"
    ' second.Logout = "15:01" (note 2nd shift has no logout)
    second.DateString = "4/1/2013"

    tc.Shift = New Shift(1) {first, second}

You can easily serialize the TimeCollection object like so:

tc.Serialize(New FileInfo("C:\SomePath\TimeCollectionA.xml"))

which creates the following contents:

   <?xml version="1.0" encoding="utf-8"?>
<TimeCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Path>C:\Temp\Tc.xml</Path>
  <Shifts>
    <Shift>
      <Name>Philipp</Name>
      <DateString>3/31/2013</DateString>
      <Login>14:11</Login>
      <Logout>14:45</Logout>
    </Shift>
    <Shift>
      <Name>Phillip</Name>
      <DateString>4/1/2013</DateString>
      <Login>14:09</Login>
    </Shift>
  </Shifts>
</TimeCollection>

Then, to deserialize the contents and turn the file back into a collection of objects you can do something with:

    Dim tc As TimeCollection
    tc = TimeCollection.Deserialize(New FileInfo("C:\SomePath\TimeCollectionA.xml"))

Now you can iterate through the tc.Shift array, etc.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.