Best practice using XmlReader?

olsonpm

Well-known member
Joined
Aug 24, 2010
Messages
46
Programming Experience
Beginner
What I am looking for is for someone to let me know whether I went about it the correct way or not, since my confidence is on the fence for this one.

Currently I have some classes that need to be de/serialized, and due to possible versioning issues, I decided to use IXmlSerializable. Here's what my write/readxml methods look like

VB.NET:
Public Sub WriteXml(ByVal writer As XmlWriter) Implements IXmlSerializable.WriteXml
    writer.WriteElementString("sVar", sVar)
    writer.WriteElementString("iVar", CStr(iVar))
    For Each aClass As Class1 In lstClass1
        writer.WriteStartElement("Class1")
        aClass.WriteXml(writer)
        writer.WriteEndElement()
    Next
End Sub

Public Sub ReadXml(ByVal reader As XmlReader) Implements IXmlSerializable.ReadXml
    Dim endNodeReached As Boolean = False
    While (Not endNodeReached)
        Select Case reader.Name
            Case "Class1"
                Dim cls1 As New Class1()
                cls1.ReadXml(reader)
                lstClass1.Add(cls1)
            Case "sVar"
                sVar= reader.ReadElementContentAsString()
            Case "iVar"
                iVar= reader.ReadElementContentAsInt()
            Case "Car"
                If (reader.NodeType = XmlNodeType.EndElement) Then
                    endNodeReached = True
                End If
                reader.Read()
            Case Else
                reader.Read()
        End Select
    End While
End Sub

Edit: The class structure looks like the following
Car
- sVar as string
- iVar as integer
- lstClass1 as List(of Class1)

Now here are my questions.
Writer
- Is the for loop fine? I would like the declaration of the startElement to be inside the Class1 itself, but didn't know how to go about that because it would conflict with the serialization process - where the class name is already declared inside the namespace.

Reader
- Is there a more standard while loop to execute this functionality? The reason I liked the endNodeReached boolean is because then I was able to traverse through sub classes (as seen in my passing of the reader to Class1).
- Is there a more logical way to traverse the xml besides using the names for each case statement?
- Does my traversal into subclasses such as Class1 make sense? I don't like how the name is hardcoded, but the only solution I could think of was to make a property inside Class1 which declared its official XML name, then grabbing that instead. However, I didn't want to deal with the extra property, and stuck with the standard "whatever the class is called, will be its xml name"

thanks for your help,
Phil
 
Last edited:
What seems to be the general idea is that the class doesn't write its own root element, the serializer does this based on class name or XmlRoot attribute. While when reading the class should read the root element and also the end root element. When you call the WriteXml method directly like that (in Car class) you get the problem that you also have to write the child objects root elements, which you can do, but you can also simply utilize the serialization engine like this, Car.WriteXml:
VB.NET:
writer.WriteElementString("sVar", sVar)
writer.WriteElementString("iVar", CStr(iVar))
Dim ser As New Xml.Serialization.XmlSerializer(GetType(Class1))
For Each aClass As Class1 In lstClass1
    ser.Serialize(writer, aClass)
Next
Car.ReadXml:
VB.NET:
reader.ReadStartElement() 'root
Dim ser As New Xml.Serialization.XmlSerializer(GetType(Class1))
While Not reader.EOF
    Select Case reader.Name
        Case "Class1"
            lstClass1.Add(CType(ser.Deserialize(reader), Class1))
        Case "sVar"
            sVar = reader.ReadElementContentAsString()
        Case "iVar"
            iVar = reader.ReadElementContentAsInt()
        Case "Car"
            Exit While
        Case Else
            '?
    End Select
End While
reader.ReadEndElement() 'root

If the order of elements doesn't change, ie it is your class that generates it, then the simple elements doesn't have to be part of that loop, and the loop may also be simplified:
VB.NET:
reader.ReadStartElement() 'root
sVar = reader.ReadElementContentAsString("sVar", "")
iVar = reader.ReadElementContentAsInt("iVar", "")
Dim ser As New Xml.Serialization.XmlSerializer(GetType(Class1))
Do Until reader.NodeType = XmlNodeType.EndElement
    lstClass1.Add(CType(ser.Deserialize(reader), Class1))
Loop
reader.ReadEndElement() 'root
Another benefit of using XmlSerializer to serialize child objects is that these classes no longer need to implement custom IXmlSerializable serialization, you remove the dependency on the ReadXml/WriteXml methods. These interface implementations may also be hidden (Private), the XmlSerializer will still use them.
 
Back
Top