Search code examples
vb.netixmlserializable

How to implement IXmlSerializable.ReadXml in case of using AbstractFactory-Pattern and Polymorphism


I'm using an AbstractFactory and polymorphism in my project and need to de-serialize xml to the correct type depending on a xml-element under the parent.

To be more specific (some pseudo code for explanation):

Public Interface IAnimal
              Inherits IXmlSerializable

   Public Property Name as String
   Public Property Age as Integer
   Public ReadOnly Property Type as AnimalType 'actually this is en Enum
End Interface

Public Interface IAnimalFactory
   Public Function Breed(animalType as AnimalType) as IAnimal
End Interface

Public Class AnimalFactoryImpl
             Implements IAnimalFactory
   Public Function Breed(animalType as AnimalType) as IAnimal
      Select Case animalType
         case ...
            return new Dog()
      End Select
   End Function
End Class

Public Mustinherit Class AnimalBaseImpl
                         Implement IAnimal
   'do all the general stuff common to all animals here

   Public MustOverride Sub Talk(words As String)

   'implement IXmlSerializable.ReadXml here
   'implement IXmlSerializable.WriteXml here
End Class

Public Class Dog
             Inherits AnimalBaseImpl
   'do dog specifics here
   'implement Talk() here
End Class

Public Class Cat
             Inherits AnimalBaseImpl
   'do cat specifics here
   'implement Talk() here
End Class

Public Class Cow
             Inherits AnimalBaseImpl
   'do cowspecifics here
   'implement Talk() here
End Class

the xml that I need/have looks like his

<animal>
   <animalType>Dog</animalType>
   <name>Snoopy</name>
   <age>62</age>   
</animal>

It's easy to implement the WriteXml method. However, the ReadXml is giving me headaches.

So far I have included the de-serialization code in the parent object (e.g. Farm). I read all the elements from within the animal tag and then call animalFactory to create the correct type depending on the animalType.

I think that this is really not nice code and it really should go into the AnimalBaseImpl or the factory but I'm at a loss how to do this as new AnimalBaseImpl() is the first thing that will happen when de-serializing...

Any hints and trick welcome :-)


Solution

  • OK, after thinking for a bit more I came up with the solution myself. Quite easy once you get there ;-)

    Since I'm using the Factory Pattern it's the Factory that needs to implement the deserialization. This is a creational pattern after all. This means, that ALL the creation methods should go into that factory. And deserializing is a creation method.

    All i need to do is pass the XmlReader object to the factory and expect a return of whatever the Factory creates.

    To stay with the above code example:

    Public Interface IAnimalFactory
       Public Function Breed(animalType as AnimalType) as IAnimal
       Public Function XmlDeserializeAnimal(reader As XmlReader) As IAnimal
    End Interface
    
    Public Class AnimalFactoryImpl
                 Implements IAnimalFactory
       Public Function Breed(animalType as AnimalType) as IAnimal
          Select Case animalType
             case ...
                return new Dog()
          End Select
       End Function
    
       Public Function XmlDeserializeAnimal(reader As XmlReader) As IAnimal implements IAnimalFactory.XmlDeserializeAnimal
          'read all the tags here inkl. animalType
          Dim returnAnimal as IAnimal = Me.Breed(animalType)
          'set name and age here as per xml
          Return returnAnimal
    End Class
    

    Now this can easily be called from withing the container object (e.g. Farm) which also implements IXmlSerializable. And all the container class needs to know of is the IAnimalFactory and the XmlDeserializeAnimal method.

    Rather straight forward if you think about it (^_~)