During Runtime Dynamic Casting

false74

Well-known member
Joined
Aug 4, 2010
Messages
76
Programming Experience
Beginner
I am working on creating a XMLSerializable Dictionary. My issue is that the objects that I am storing in my dictionary are not the same type but are inherited from the same class. So here is an example class structure of inheritance:

Person (Base Class)
Teacher (Inherits Person)​
Student (Inherits Person)​
Principle (Inherits Person)​


So I created a new Dictionary of "Person" so that I can store not only type Person but also Teacher, Student, and Principle. I made my own Dictionary class so I can control how to serialize the data into XML. (Note: each element [person] is already serializable). Because I am trying to store different data types in the same collection how should I go about deserializing them, below is how I intended to solve my issue (See readXML method) but you cannot dynamically cast during runtime.

VB.NET:
Public Class PersonDictionary
    Inherits Dictionary(Of Int32, Person)
    Implements IXmlSerializable

    Private _entry As String = "entry"
    Public Property EntryElementName As String
        Set(ByVal value As String)
            _entry = value
        End Set
        Get
            Return _entry
        End Get
    End Property

    Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements IXmlSerializable.GetSchema
        Return Nothing
    End Function

    Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements IXmlSerializable.ReadXml
        Dim wasEmpty As Boolean = reader.IsEmptyElement

        reader.Read()

        If wasEmpty Then
            Return
        End If

        While reader.NodeType <> System.Xml.XmlNodeType.EndElement
            reader.ReadStartElement(_entry)

            Dim entrytype As Type
            Select Case reader.Name
                Case "Teacher"
                    entrytype = GetType(Teacher)
               Case "Student"
                    entrytype = GetType(Student)
               Case "Principle"
                    entrytype = GetType(Principle)
                Case Else
                    entrytype = GetType(Person)
            End Select

            Dim entrySerializer As New XmlSerializer(entrytype)
            Dim keySerializer As New XmlSerializer(entrytype)

            Dim key = CType(keySerializer.Deserialize(reader), Int32)
            [B]Dim value = CType(entrySerializer.Deserialize(reader), entrytype) ' i cannot dynamically cast at runtime![/B]

            Me.Add(key, value)

            reader.ReadEndElement()
            reader.MoveToContent()
        End While

        reader.ReadEndElement()
    End Sub

    Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements IXmlSerializable.WriteXml
        Dim ns As New XmlSerializerNamespaces 'remove namespaces
        ns.Add("", "")
        Dim keySerializer As New XmlSerializer(GetType(Int32))
        For Each key As Int32 In Me.Keys
            Dim valueSerializer As New XmlSerializer(Me(key).Type)
            writer.WriteStartElement(_entry)

            keySerializer.Serialize(writer, key, ns)
            valueSerializer.Serialize(writer, Me(key), ns)

            writer.WriteEndElement()
        Next
    End Sub
End Class
 
Dim value = CType(entrySerializer.Deserialize(reader), entrytype) ' i cannot dynamically cast at runtime!
Cast as type Person, that does not affect the actual derived object, it is only so that you can add the object to the strongly typed dictionary.

There is also another problem with your deserializer code, after reading the _entry start element you must read the key, keySerializer must also be using correct type (Integer). Then you can proceed to read entrytype and deserialize that value.
 
I cannot cast as type Person because each of the child class (teacher, student, etc) has additional properties and a different root name and the deserialzier does not know what to do with them. I ended up just creating a switch statement (select case) that just makes a new XMLSerializer per case then adding that value into the dictionary.
VB.NET:
            '... read xml etc etc
            Dim value as Person = nothing
            Select Case reader.Name
                Case "Student"
                    Dim entrySerializer As New XmlSerializer(GetType(Student))
                    value = CType(entrySerializer.Deserialize(reader), Student)
                Case "Teacher"
                    Dim entrySerializer As New XmlSerializer(GetType(Teacher))
                    value = CType(entrySerializer.Deserialize(reader), Teacher)
                Case "Principle"
                    Dim entrySerializer As New XmlSerializer(GetType(Principle))
                    value = CType(entrySerializer.Deserialize(reader), Principle)
                Case "Person"
                    Dim entrySerializer As New XmlSerializer(GetType(Person))
                    value = CType(entrySerializer.Deserialize(reader), Person)
            End Select
            '... add to dictionary etc etc
 
The question was not about serialization, but about how you could cast the deserialized object. As I said, you can also cast the object, whatever derived type that may be, to base type Person. That is the type of the values the dictionary expect to be added. Whether they are that type specifically or derived from that type does not matter. It also does not matter if you reference a derived object in a base type variable, which is what casting is all about.
 
Back
Top